Compare commits
5 Commits
9413fba265
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ae0593c972 | |||
| f966a1239e | |||
| 15ecefb5fb | |||
| 897775f63a | |||
| fadecc7c95 |
@@ -3,8 +3,6 @@ target = "riscv64.json"
|
||||
|
||||
[unstable]
|
||||
json-target-spec = true
|
||||
build-std = ["core", "compiler_builtins", "alloc"]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
|
||||
[target.riscv64]
|
||||
rustflags = [
|
||||
|
||||
8
.gdbinit
8
.gdbinit
@@ -1,6 +1,6 @@
|
||||
file target/riscv64/debug/kernel-rust
|
||||
# file target/riscv64/debug/kernel-rust
|
||||
target remote localhost:1234
|
||||
break machine_mode_entry
|
||||
# break *0x800bf000
|
||||
# add-symbol-file target/riscv64/debug/test_pic 0x800bf000
|
||||
# break machine_mode_entry
|
||||
break *0x800cfdd8
|
||||
add-symbol-file target/riscv64/debug/agetty 0x800cfdd8
|
||||
c
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,3 +7,6 @@
|
||||
disk.img
|
||||
**/*.mem
|
||||
mnt
|
||||
|
||||
sysroot/lib/rustlib/riscv64
|
||||
library/alloc
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/bytes-struct","crates/io","crates/std", "crates/shared", "user/*"]
|
||||
members = ["crates/ansii","user/*"]
|
||||
exclude = ["library", "build-tools", "crates/io"]
|
||||
|
||||
[package]
|
||||
name = "kernel-rust"
|
||||
@@ -12,10 +13,11 @@ bitflags = "2"
|
||||
embedded-alloc = "0.7"
|
||||
kernel-macros = { path = "crates/kernel-macros" }
|
||||
bytes-struct = { path = "crates/bytes-struct" }
|
||||
ansii = { path = "crates/ansii" }
|
||||
log = "0.4"
|
||||
critical-section = { version = "1", features = ["restore-state-bool"] }
|
||||
bffs = { path = "crates/bffs", features = ["alloc"] }
|
||||
io = { path = "crates/io", features = ["alloc"] }
|
||||
io = { package = "no-std-io", path = "crates/io" }
|
||||
shared = { path = "crates/shared", features = ["kernel"] }
|
||||
goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
|
||||
hashbrown = "0.16"
|
||||
|
||||
2
build-tools/.cargo/config.toml
Normal file
2
build-tools/.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "x86_64-unknown-linux-gnu"
|
||||
14
build-tools/Cargo.toml
Normal file
14
build-tools/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "build-tools"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "gen-symbols"
|
||||
path = "src/gen_symbols.rs"
|
||||
|
||||
[dependencies]
|
||||
rayon = "1.11"
|
||||
object = "0.32"
|
||||
addr2line = "0.21"
|
||||
rustc-demangle = "0.1"
|
||||
107
build-tools/src/gen_symbols.rs
Normal file
107
build-tools/src/gen_symbols.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use addr2line::Context;
|
||||
use object::{Object, ObjectSymbol, SymbolKind};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[repr(C, packed(4))]
|
||||
#[derive(Debug)]
|
||||
struct RawSymbol {
|
||||
addr: u64,
|
||||
line: u32,
|
||||
name_off: u32,
|
||||
file_off: u32,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let elf_path = "../target/riscv64/debug/kernel-rust";
|
||||
let bin_data = std::fs::read(elf_path)?;
|
||||
let obj_file = object::File::parse(&*bin_data)?;
|
||||
|
||||
let context = Context::new(&obj_file)?;
|
||||
|
||||
let mut symbols_list = Vec::new();
|
||||
let mut string_table = Vec::new();
|
||||
let mut str_cache = HashMap::new();
|
||||
|
||||
// Helper pour gérer la table des chaînes (String Table)
|
||||
let mut add_string = |s: &str| -> u32 {
|
||||
*str_cache.entry(s.to_string()).or_insert_with(|| {
|
||||
let off = string_table.len() as u32;
|
||||
string_table.extend_from_slice(s.as_bytes());
|
||||
string_table.push(0); // Null terminator
|
||||
off
|
||||
})
|
||||
};
|
||||
|
||||
println!("Extraction des symboles depuis {}...", elf_path);
|
||||
|
||||
obj_file.symbols().enumerate().for_each(|(_i, sym)| {
|
||||
// On ne garde que les fonctions (Text)
|
||||
if sym.kind() == SymbolKind::Text && sym.size() > 0 {
|
||||
let addr = sym.address();
|
||||
let raw_name = sym.name().unwrap_or("unknown");
|
||||
let name = format!("{:#}", rustc_demangle::demangle(raw_name));
|
||||
|
||||
let mut frames = context.find_frames(addr).skip_all_loads().unwrap();
|
||||
|
||||
let (file, line) = if let Ok(Some(frame)) = frames.next() {
|
||||
let f = frame
|
||||
.location
|
||||
.as_ref()
|
||||
.and_then(|l| {
|
||||
l.file.and_then(|file| {
|
||||
Path::new(file)
|
||||
.strip_prefix(std::env::current_dir().unwrap().parent().unwrap())
|
||||
.map_or(Some(file), |p| p.to_str())
|
||||
})
|
||||
})
|
||||
.unwrap_or("unknown");
|
||||
let l = frame.location.as_ref().and_then(|l| l.line).unwrap_or(0);
|
||||
(f, l)
|
||||
} else {
|
||||
("unknown", 0)
|
||||
};
|
||||
|
||||
symbols_list.push(RawSymbol {
|
||||
addr,
|
||||
line,
|
||||
name_off: add_string(&name),
|
||||
file_off: add_string(file),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Tri par adresse pour la recherche binaire au runtime
|
||||
symbols_list.sort_by_key(|s| s.addr);
|
||||
|
||||
// Écriture du fichier symbols.bin
|
||||
let mut f = BufWriter::new(File::create("../target/symbols.bin")?);
|
||||
|
||||
// Header : [u64: count] [u64: string_table_offset]
|
||||
let header_size = 16;
|
||||
let sym_table_size = symbols_list.len() * std::mem::size_of::<RawSymbol>();
|
||||
f.write_all(&(symbols_list.len() as u64).to_le_bytes())?;
|
||||
f.write_all(&((header_size + sym_table_size) as u64).to_le_bytes())?;
|
||||
|
||||
// Table des symboles
|
||||
for sym in &symbols_list {
|
||||
unsafe {
|
||||
let bytes = std::slice::from_raw_parts(
|
||||
(sym as *const RawSymbol) as *const u8,
|
||||
std::mem::size_of::<RawSymbol>(),
|
||||
);
|
||||
f.write_all(bytes)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Table des noms
|
||||
f.write_all(&string_table)?;
|
||||
|
||||
println!(
|
||||
"Terminé ! {} symboles écrits dans symbols.bin",
|
||||
symbols_list.len()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
7
crates/ansii/Cargo.toml
Normal file
7
crates/ansii/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "ansii"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
winnow = { version = "1", default-features = false, features = ["binary", "ascii"] }
|
||||
66
crates/ansii/src/lib.rs
Normal file
66
crates/ansii/src/lib.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
#![no_std]
|
||||
|
||||
use winnow::Result;
|
||||
use winnow::ascii::dec_uint;
|
||||
use winnow::combinator::{alt, preceded, seq};
|
||||
use winnow::error::ContextError;
|
||||
use winnow::prelude::*;
|
||||
|
||||
pub enum AnsiiEscape {
|
||||
Color16(ColorPlace, Color16Type, u8),
|
||||
Color256(u8),
|
||||
ColorRGB(ColorPlace, u8, u8, u8),
|
||||
}
|
||||
pub enum ColorPlace {
|
||||
Foreground,
|
||||
Background,
|
||||
}
|
||||
pub enum Color16Type {
|
||||
Normal,
|
||||
Bold,
|
||||
Brigth,
|
||||
}
|
||||
|
||||
fn parse_color16(input: &mut &str) -> Result<AnsiiEscape> {
|
||||
let (c, _) = preceded("[", seq!(dec_uint, "m")).parse_next(input)?;
|
||||
if c < 30 || c == 38 || c == 48 || c > 49 {
|
||||
Err(ContextError::new())
|
||||
} else {
|
||||
Ok(AnsiiEscape::Color16(
|
||||
ColorPlace::Foreground,
|
||||
Color16Type::Normal,
|
||||
c,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_color_rgb(input: &mut &str) -> Result<AnsiiEscape> {
|
||||
let (t, _, r, _, g, _, b, _) = preceded(
|
||||
"[",
|
||||
seq!(
|
||||
alt(("38", "48")),
|
||||
";",
|
||||
dec_uint,
|
||||
";",
|
||||
dec_uint,
|
||||
";",
|
||||
dec_uint,
|
||||
"m"
|
||||
),
|
||||
)
|
||||
.parse_next(input)?;
|
||||
Ok(AnsiiEscape::ColorRGB(
|
||||
if t == "38" {
|
||||
ColorPlace::Foreground
|
||||
} else {
|
||||
ColorPlace::Background
|
||||
},
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn parse_ansii(input: &mut &str) -> Result<AnsiiEscape> {
|
||||
alt((parse_color_rgb, parse_color16)).parse_next(input)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
io = { path = "../io" }
|
||||
io = { package = "no-std-io", path = "../io" }
|
||||
bitflags = "2"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::error::Error;
|
||||
use io::{Read, ReadLeExt};
|
||||
use io::Read;
|
||||
|
||||
use crate::io_ext::ReadLeExt;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Fat32BootSector {
|
||||
@@ -94,7 +95,7 @@ impl Fat32BootSector {
|
||||
}
|
||||
|
||||
impl Fat32BootSector {
|
||||
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, Error<T::Error>> {
|
||||
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, io::Error> {
|
||||
let mut jump_boot = [0u8; _];
|
||||
disk.read_exact(&mut jump_boot)?;
|
||||
let mut oem_name = [0u8; _];
|
||||
|
||||
@@ -1,36 +1,32 @@
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
entry::{DirEntry, DirectoryIterator},
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
path::Path,
|
||||
};
|
||||
use io::{self, IoBase, Read, Seek, Write};
|
||||
use io::{self, Read, Seek, Write};
|
||||
|
||||
pub struct Dir<'a, T> {
|
||||
raw: RawFile<'a, T>,
|
||||
fs: &'a Fat32FileSystem<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> IoBase for Dir<'a, T> {
|
||||
type Error = Error<T::Error>;
|
||||
}
|
||||
impl<'a, T: ReadSeek> Seek for Dir<'a, T> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
self.raw.seek(pos)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadSeek> Read for Dir<'a, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.raw.read(buf)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadWriteSeek> Write for Dir<'a, T> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.raw.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.raw.flush()
|
||||
}
|
||||
}
|
||||
@@ -44,7 +40,7 @@ impl<'a, T> Dir<'a, T> {
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'a, T>, Error<T::Error>> {
|
||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'a, T>, io::Error> {
|
||||
if path.as_ref().is_absolute() {
|
||||
return self.fs.open_entry(path);
|
||||
}
|
||||
@@ -56,16 +52,16 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
if f.is_dir() {
|
||||
return f.to_dir().open_entry(entry_name);
|
||||
} else {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
} else {
|
||||
return Ok(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::NotFound)
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))
|
||||
}
|
||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'a, T>, Error<T::Error>> {
|
||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'a, T>, io::Error> {
|
||||
if path.as_ref().is_absolute() {
|
||||
return self.fs.open_file(path);
|
||||
}
|
||||
@@ -74,19 +70,19 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
match dirname {
|
||||
Some(name) => {
|
||||
if !entry.is_dir() {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
entry.to_dir().open_file(name)
|
||||
}
|
||||
None => {
|
||||
if !entry.is_file() {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
Ok(entry.to_file())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Self, Error<T::Error>> {
|
||||
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Self, io::Error> {
|
||||
let path = path.as_ref();
|
||||
if path.is_absolute() {
|
||||
return self.fs.open_dir(path);
|
||||
@@ -94,7 +90,7 @@ impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
let (start, dirname) = path.split_path();
|
||||
let entry = self.open_entry(start)?;
|
||||
if !entry.is_dir() {
|
||||
return Err(Error::NotFound);
|
||||
return Err(io::Error::from(io::ErrorKind::NotFound));
|
||||
}
|
||||
match dirname {
|
||||
Some(name) => entry.to_dir().open_dir(name),
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use core::{array::IntoIter, iter::Copied, str::Utf8Error};
|
||||
use core::{char::DecodeUtf16, marker::PhantomData, slice::Iter};
|
||||
|
||||
use crate::io_ext::ReadLeExt;
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek,
|
||||
consts::FATAttr,
|
||||
dir::Dir,
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
};
|
||||
|
||||
use io::{IoBase, Read, ReadLeExt};
|
||||
use io::Read;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::borrow::ToOwned;
|
||||
@@ -33,7 +33,7 @@ pub struct FatDirEntry {
|
||||
}
|
||||
|
||||
impl FatDirEntry {
|
||||
pub fn deserialize<T: Read>(reader: &mut T) -> Result<Self, T::Error> {
|
||||
pub fn deserialize<T: Read>(reader: &mut T) -> Result<Self, io::Error> {
|
||||
let mut name = [0u8; _];
|
||||
reader.read_exact(&mut name)?;
|
||||
let attr = reader.read_u8()?;
|
||||
@@ -131,7 +131,7 @@ pub struct LongFileNameBuilder<T> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: IoBase> LongFileNameBuilder<T> {
|
||||
impl<T> LongFileNameBuilder<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: [0; _],
|
||||
@@ -160,12 +160,12 @@ impl<T: IoBase> LongFileNameBuilder<T> {
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn to_string(&self) -> Result<String, Error<T::Error>> {
|
||||
pub fn to_string(&self) -> Result<String, io::Error> {
|
||||
self.iter().try_collect::<String>()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IoBase> IntoIterator for LongFileNameBuilder<T> {
|
||||
impl<T> IntoIterator for LongFileNameBuilder<T> {
|
||||
type Item = <LongFileNameIterator<T, IntoIter<u16, 256>> as Iterator>::Item;
|
||||
|
||||
type IntoIter = LongFileNameIterator<T, IntoIter<u16, 256>>;
|
||||
@@ -178,7 +178,7 @@ impl<T: IoBase> IntoIterator for LongFileNameBuilder<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IoBase> LongFileNameBuilder<T> {
|
||||
impl<T> LongFileNameBuilder<T> {
|
||||
pub fn iter(&self) -> LongFileNameIterator<T, Copied<Iter<'_, u16>>> {
|
||||
LongFileNameIterator {
|
||||
iterator: char::decode_utf16(self.inner.iter().copied()),
|
||||
@@ -192,8 +192,8 @@ pub struct LongFileNameIterator<T, I: Iterator<Item = u16>> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: IoBase, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I> {
|
||||
type Item = Result<char, Error<T::Error>>;
|
||||
impl<T, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I> {
|
||||
type Item = Result<char, io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iterator.next()? {
|
||||
@@ -204,12 +204,15 @@ impl<T: IoBase, I: Iterator<Item = u16>> Iterator for LongFileNameIterator<T, I>
|
||||
Some(Ok(value))
|
||||
}
|
||||
}
|
||||
Err(_) => Some(Err(Error::UnsupportedFileNameCharacter)),
|
||||
Err(_) => Some(Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"Unsupported file name character",
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IoBase> Default for LongFileNameBuilder<T> {
|
||||
impl<T> Default for LongFileNameBuilder<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
@@ -226,7 +229,7 @@ impl<'a, T> DirectoryIterator<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T: ReadSeek + 'a> Iterator for DirectoryIterator<'a, T> {
|
||||
type Item = Result<DirEntry<'a, T>, Error<T::Error>>;
|
||||
type Item = Result<DirEntry<'a, T>, io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut lfn_builder = LongFileNameBuilder::new();
|
||||
@@ -283,7 +286,7 @@ pub struct DirEntry<'a, T> {
|
||||
fs: &'a Fat32FileSystem<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> DirEntry<'a, T> {
|
||||
impl<'a, T> DirEntry<'a, T> {
|
||||
fn new(
|
||||
entry: FatDirEntry,
|
||||
short_name: [u8; 11],
|
||||
@@ -298,13 +301,16 @@ impl<'a, T: IoBase> DirEntry<'a, T> {
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn name(&self) -> Result<String, Error<T::Error>> {
|
||||
pub fn name(&self) -> Result<String, io::Error> {
|
||||
if let Some(long_name) = self.long_name() {
|
||||
long_name.to_string()
|
||||
} else {
|
||||
self.short_name()
|
||||
.map(|n| n.to_owned())
|
||||
.map_err(|_| Error::UnsupportedFileNameCharacter)
|
||||
self.short_name().map(|n| n.to_owned()).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"Unsupported file name character",
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn long_name(&self) -> Option<&LongFileNameBuilder<T>> {
|
||||
|
||||
@@ -1,79 +1 @@
|
||||
use io::error::IoError;
|
||||
|
||||
/// Error enum with all errors that can be returned by functions from this crate
|
||||
///
|
||||
/// Generic parameter `T` is a type of external error returned by the user provided storage
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Error<T> {
|
||||
/// A user provided storage instance returned an error during an input/output operation.
|
||||
Io(T),
|
||||
/// A read operation cannot be completed because an end of a file has been reached prematurely.
|
||||
UnexpectedEof,
|
||||
/// A write operation cannot be completed because `Write::write` returned 0.
|
||||
WriteZero,
|
||||
/// A parameter was incorrect.
|
||||
InvalidInput,
|
||||
/// A requested file or directory has not been found.
|
||||
NotFound,
|
||||
/// A file or a directory with the same name already exists.
|
||||
AlreadyExists,
|
||||
/// An operation cannot be finished because a directory is not empty.
|
||||
DirectoryIsNotEmpty,
|
||||
/// File system internal structures are corrupted/invalid.
|
||||
CorruptedFileSystem,
|
||||
/// There is not enough free space on the storage to finish the requested operation.
|
||||
NotEnoughSpace,
|
||||
/// The provided file name is either too long or empty.
|
||||
InvalidFileNameLength,
|
||||
/// The provided file name contains an invalid character.
|
||||
UnsupportedFileNameCharacter,
|
||||
/// The file content contains invalid UTF-8 characters.
|
||||
InvalidUTF8,
|
||||
}
|
||||
|
||||
impl<T: IoError> From<T> for Error<T> {
|
||||
fn from(error: T) -> Self {
|
||||
Error::Io(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::fmt::Display> core::fmt::Display for Error<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Error::Io(io_error) => write!(f, "IO error: {}", io_error),
|
||||
Error::UnexpectedEof => write!(f, "Unexpected end of file"),
|
||||
Error::NotEnoughSpace => write!(f, "Not enough space"),
|
||||
Error::WriteZero => write!(f, "Write zero"),
|
||||
Error::InvalidInput => write!(f, "Invalid input"),
|
||||
Error::InvalidFileNameLength => write!(f, "Invalid file name length"),
|
||||
Error::UnsupportedFileNameCharacter => write!(f, "Unsupported file name character"),
|
||||
Error::DirectoryIsNotEmpty => write!(f, "Directory is not empty"),
|
||||
Error::NotFound => write!(f, "No such file or directory"),
|
||||
Error::AlreadyExists => write!(f, "File or directory already exists"),
|
||||
Error::CorruptedFileSystem => write!(f, "Corrupted file system"),
|
||||
Error::InvalidUTF8 => write!(f, "File contains invalid UTF-8 characters"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::fmt::Debug + IoError> IoError for Error<T> {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
match self {
|
||||
Error::<T>::Io(io_error) => io_error.is_interrupted(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn new_unexpected_eof_error() -> Self {
|
||||
Error::<T>::UnexpectedEof
|
||||
}
|
||||
|
||||
fn new_write_zero_error() -> Self {
|
||||
Error::<T>::WriteZero
|
||||
}
|
||||
|
||||
fn new_invalid_utf8_error() -> Self {
|
||||
Error::<T>::InvalidUTF8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
consts::{FAT32_BAD_CLUSTER, FAT32_END_OF_CHAIN},
|
||||
error::Error,
|
||||
};
|
||||
|
||||
use io::{self, IoBase, Read, Seek, Write};
|
||||
use io::{self, Read, Seek, Write};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RawFile<'a, T> {
|
||||
@@ -30,12 +29,8 @@ impl<'a, T> RawFile<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> IoBase for RawFile<'a, T> {
|
||||
type Error = Error<T::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: ReadSeek> Seek for RawFile<'a, T> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
let new_pos = match pos {
|
||||
io::SeekFrom::Start(s) => s,
|
||||
io::SeekFrom::Current(c) => (self.pos as i64 + c) as u64,
|
||||
@@ -67,7 +62,7 @@ impl<'a, T: ReadSeek> Seek for RawFile<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T: ReadSeek> Read for RawFile<'a, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
let max_len = self
|
||||
.size
|
||||
.map(|size| core::cmp::min(buf.len(), size as usize - self.pos as usize))
|
||||
@@ -116,11 +111,11 @@ impl<'a, T: ReadSeek> Read for RawFile<'a, T> {
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadWriteSeek> Write for RawFile<'a, T> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, io::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -132,25 +127,22 @@ pub struct File<'a, T> {
|
||||
fs: &'a Fat32FileSystem<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> IoBase for File<'a, T> {
|
||||
type Error = Error<T::Error>;
|
||||
}
|
||||
impl<'a, T: ReadSeek> Seek for File<'a, T> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, io::Error> {
|
||||
self.raw.seek(pos)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadSeek> Read for File<'a, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
self.raw.read(buf)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadWriteSeek> Write for File<'a, T> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
self.raw.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
self.raw.flush()
|
||||
}
|
||||
}
|
||||
|
||||
27
crates/bffs/src/io_ext.rs
Normal file
27
crates/bffs/src/io_ext.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use io::{Read, Result};
|
||||
|
||||
pub trait ReadLeExt {
|
||||
fn read_u8(&mut self) -> Result<u8>;
|
||||
fn read_u16_le(&mut self) -> Result<u16>;
|
||||
fn read_u32_le(&mut self) -> Result<u32>;
|
||||
}
|
||||
|
||||
impl<T: Read> ReadLeExt for T {
|
||||
fn read_u8(&mut self) -> Result<u8> {
|
||||
let mut buf = [0_u8; 1];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
|
||||
fn read_u16_le(&mut self) -> Result<u16> {
|
||||
let mut buf = [0_u8; 2];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
fn read_u32_le(&mut self) -> Result<u32> {
|
||||
let mut buf = [0_u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,15 @@
|
||||
#![feature(iterator_try_collect, iter_order_by)]
|
||||
#![allow(unused_features)]
|
||||
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::fmt::Display;
|
||||
|
||||
use crate::{
|
||||
boot_sector::Fat32BootSector,
|
||||
consts::FAT32_CLUSTER_MASK,
|
||||
dir::Dir,
|
||||
entry::{DirEntry, FatEntry},
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
path::Path,
|
||||
boot_sector::Fat32BootSector, consts::FAT32_CLUSTER_MASK, dir::Dir, entry::{DirEntry, FatEntry}, file::{File, RawFile}, io_ext::ReadLeExt, path::Path
|
||||
};
|
||||
|
||||
use io::{Read, ReadLeExt, Seek, Write};
|
||||
use io::{Read, Seek, Write};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
@@ -23,8 +18,8 @@ pub mod boot_sector;
|
||||
pub mod consts;
|
||||
pub mod dir;
|
||||
pub mod entry;
|
||||
pub mod error;
|
||||
pub mod file;
|
||||
pub mod io_ext;
|
||||
pub mod path;
|
||||
|
||||
pub trait ReadSeek: Read + Seek {}
|
||||
@@ -46,7 +41,7 @@ pub struct Fat32FsInfo {
|
||||
}
|
||||
|
||||
impl Fat32FsInfo {
|
||||
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, Error<T::Error>> {
|
||||
pub fn deserialize<T: Read>(disk: &mut T) -> Result<Self, io::Error> {
|
||||
let lead_signature = disk.read_u32_le()?;
|
||||
let mut reserved1 = [0u8; _];
|
||||
disk.read_exact(&mut reserved1)?;
|
||||
@@ -81,7 +76,7 @@ impl<T> Display for Fat32FileSystem<T> {
|
||||
}
|
||||
|
||||
impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||
pub fn new(mut device: T) -> Result<Self, Error<T::Error>> {
|
||||
pub fn new(mut device: T) -> Result<Self, io::Error> {
|
||||
device.seek(io::SeekFrom::Start(0))?;
|
||||
|
||||
let boot_sector = Fat32BootSector::deserialize(&mut device)?;
|
||||
@@ -91,7 +86,7 @@ impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||
})
|
||||
}
|
||||
/// Get the next cluster from the current one
|
||||
fn get_next_cluster(&self, current_cluster: u32) -> Result<u32, Error<T::Error>> {
|
||||
fn get_next_cluster(&self, current_cluster: u32) -> Result<u32, io::Error> {
|
||||
let fat_offset =
|
||||
self.fat_start_offset() + (current_cluster as u64 * size_of::<FatEntry>() as u64);
|
||||
self.device
|
||||
@@ -136,15 +131,15 @@ impl<T> Fat32FileSystem<T> {
|
||||
}
|
||||
}
|
||||
impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'_, T>, Error<T::Error>> {
|
||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'_, T>, io::Error> {
|
||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||
self.root_directory().open_entry(path)
|
||||
}
|
||||
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Dir<'_, T>, Error<T::Error>> {
|
||||
pub fn open_dir<P: AsRef<Path>>(&self, path: P) -> Result<Dir<'_, T>, io::Error> {
|
||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||
self.root_directory().open_dir(path)
|
||||
}
|
||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'_, T>, Error<T::Error>> {
|
||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'_, T>, io::Error> {
|
||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||
self.root_directory().open_file(path)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub fn main() {
|
||||
|
||||
// walk(fs.root_directory());
|
||||
|
||||
let mut f = fs.open_file("/usr/bin/test_pic").unwrap();
|
||||
let mut f = fs.open_file("/usr/bin/agetty").unwrap();
|
||||
let mut content = Vec::new();
|
||||
f.read_to_end(&mut content).unwrap();
|
||||
println!("file content len: {}", content.len());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use core::fmt::Display;
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::{borrow::Borrow, ops::Deref};
|
||||
|
||||
@@ -12,6 +13,12 @@ pub struct Path {
|
||||
inner: str,
|
||||
}
|
||||
|
||||
impl Display for Path {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{}", &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for &Path {
|
||||
fn from(value: &str) -> Self {
|
||||
unsafe { &*(value as *const str as *const Path) }
|
||||
|
||||
3
crates/io/.gitignore
vendored
Normal file
3
crates/io/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.cargo
|
||||
|
||||
target
|
||||
@@ -1,10 +1,9 @@
|
||||
[package]
|
||||
name = "io"
|
||||
name = "no-std-io"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
std = ["alloc"]
|
||||
std = []
|
||||
|
||||
49
crates/io/justfile
Normal file
49
crates/io/justfile
Normal file
@@ -0,0 +1,49 @@
|
||||
RUST_SRC := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src"
|
||||
|
||||
copy-io:
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
FILES=(
|
||||
"io/error/repr_unpacked.rs"
|
||||
"io/error/repr_bitpacked.rs"
|
||||
"io/error.rs"
|
||||
"io/buffered/mod.rs"
|
||||
"io/buffered/linewriter.rs"
|
||||
"io/buffered/linewritershim.rs"
|
||||
"io/buffered/bufwriter.rs"
|
||||
"io/buffered/bufreader.rs"
|
||||
"io/buffered/bufreader/buffer.rs"
|
||||
"io/copy.rs"
|
||||
"io/cursor.rs"
|
||||
"io/impls.rs"
|
||||
"io/prelude.rs"
|
||||
"io/util.rs"
|
||||
"io/mod.rs"
|
||||
)
|
||||
|
||||
for f in "${FILES[@]}"; do
|
||||
echo "Processing $f..."
|
||||
DEST="src/$f"
|
||||
|
||||
mkdir -p "$(dirname "$DEST")"
|
||||
|
||||
cp "{{ RUST_SRC }}/$f" "$DEST"
|
||||
sed -i -E -n '$!N; /^#\[cfg\(test\)\]\nmod tests/d; P; D' "$DEST"
|
||||
|
||||
if [[ "$f" == "io/error.rs" ]]; then
|
||||
sed -i "s/alloc::/alloc_crate::/g" "$DEST"
|
||||
fi
|
||||
|
||||
if [[ "$f" == "io/mod.rs" ]]; then
|
||||
sed -i "/mod pipe/d" "$DEST"
|
||||
sed -i "/self::pipe/d" "$DEST"
|
||||
sed -i "/mod stdio/d" "$DEST"
|
||||
sed -i "/stdio::/d" "$DEST"
|
||||
sed -i "/self::stdio/d" "$DEST"
|
||||
sed -i "/feature = \"is_terminal\"/d" "$DEST"
|
||||
sed -i "/feature = \"print_internals\"/d" "$DEST"
|
||||
sed -i "/feature = \"internal_output_capture\"/d" "$DEST"
|
||||
sed -i "/feature = \"anonymous_pipe\"/d" "$DEST"
|
||||
fi
|
||||
done
|
||||
@@ -1,48 +0,0 @@
|
||||
/// Trait that should be implemented by errors returned from the user supplied storage.
|
||||
///
|
||||
/// Implementations for `std::io::Error` and `()` are provided by this crate.
|
||||
pub trait IoError: core::fmt::Debug {
|
||||
fn is_interrupted(&self) -> bool;
|
||||
fn new_unexpected_eof_error() -> Self;
|
||||
fn new_write_zero_error() -> Self;
|
||||
fn new_invalid_utf8_error() -> Self;
|
||||
}
|
||||
|
||||
impl IoError for () {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn new_unexpected_eof_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
|
||||
fn new_write_zero_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
|
||||
fn new_invalid_utf8_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl IoError for std::io::Error {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
self.kind() == std::io::ErrorKind::Interrupted
|
||||
}
|
||||
|
||||
fn new_unexpected_eof_error() -> Self {
|
||||
Self::new(
|
||||
std::io::ErrorKind::UnexpectedEof,
|
||||
"failed to fill whole buffer",
|
||||
)
|
||||
}
|
||||
|
||||
fn new_write_zero_error() -> Self {
|
||||
Self::new(
|
||||
std::io::ErrorKind::WriteZero,
|
||||
"failed to write whole buffer",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ mod bufwriter;
|
||||
mod linewriter;
|
||||
mod linewritershim;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub use bufwriter::WriterPanicked;
|
||||
@@ -1,13 +1,11 @@
|
||||
use super::{BorrowedBuf, BufReader, BufWriter, DEFAULT_BUF_SIZE, Read, Result, Write};
|
||||
use crate::alloc::Allocator;
|
||||
use crate::cmp;
|
||||
use alloc_crate::collections::VecDeque;
|
||||
use crate::collections::VecDeque;
|
||||
use crate::io::IoSlice;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::sys::io::{CopyState, kernel_copy};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Copies the entire contents of a reader into a writer.
|
||||
///
|
||||
@@ -1,5 +1,3 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::alloc::Allocator;
|
||||
use crate::cmp;
|
||||
@@ -1,5 +1,3 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// On 64-bit platforms, `io::Error` may use a bit-packed representation to
|
||||
// reduce size. However, this representation assumes that error codes are
|
||||
@@ -1,8 +1,6 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::alloc::Allocator;
|
||||
use alloc_crate::collections::VecDeque;
|
||||
use crate::collections::VecDeque;
|
||||
use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
|
||||
use crate::{cmp, fmt, mem, str};
|
||||
|
||||
@@ -1,52 +1,338 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
//! Traits, helpers, and type definitions for core I/O functionality.
|
||||
//!
|
||||
//! The `std::io` module contains a number of common things you'll need
|
||||
//! when doing input and output. The most core part of this module is
|
||||
//! the [`Read`] and [`Write`] traits, which provide the
|
||||
//! most general interface for reading and writing input and output.
|
||||
//!
|
||||
//! ## Read and Write
|
||||
//!
|
||||
//! Because they are traits, [`Read`] and [`Write`] are implemented by a number
|
||||
//! of other types, and you can implement them for your types too. As such,
|
||||
//! you'll see a few different types of I/O throughout the documentation in
|
||||
//! this module: [`File`]s, [`TcpStream`]s, and sometimes even [`Vec<T>`]s. For
|
||||
//! example, [`Read`] adds a [`read`][`Read::read`] method, which we can use on
|
||||
//! [`File`]s:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let mut f = File::open("foo.txt")?;
|
||||
//! let mut buffer = [0; 10];
|
||||
//!
|
||||
//! // read up to 10 bytes
|
||||
//! let n = f.read(&mut buffer)?;
|
||||
//!
|
||||
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`Read`] and [`Write`] are so important, implementors of the two traits have a
|
||||
//! nickname: readers and writers. So you'll sometimes see 'a reader' instead
|
||||
//! of 'a type that implements the [`Read`] trait'. Much easier!
|
||||
//!
|
||||
//! ## Seek and BufRead
|
||||
//!
|
||||
//! Beyond that, there are two important traits that are provided: [`Seek`]
|
||||
//! and [`BufRead`]. Both of these build on top of a reader to control
|
||||
//! how the reading happens. [`Seek`] lets you control where the next byte is
|
||||
//! coming from:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::SeekFrom;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let mut f = File::open("foo.txt")?;
|
||||
//! let mut buffer = [0; 10];
|
||||
//!
|
||||
//! // skip to the last 10 bytes of the file
|
||||
//! f.seek(SeekFrom::End(-10))?;
|
||||
//!
|
||||
//! // read up to 10 bytes
|
||||
//! let n = f.read(&mut buffer)?;
|
||||
//!
|
||||
//! println!("The bytes: {:?}", &buffer[..n]);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`BufRead`] uses an internal buffer to provide a number of other ways to read, but
|
||||
//! to show it off, we'll need to talk about buffers in general. Keep reading!
|
||||
//!
|
||||
//! ## BufReader and BufWriter
|
||||
//!
|
||||
//! Byte-based interfaces are unwieldy and can be inefficient, as we'd need to be
|
||||
//! making near-constant calls to the operating system. To help with this,
|
||||
//! `std::io` comes with two structs, [`BufReader`] and [`BufWriter`], which wrap
|
||||
//! readers and writers. The wrapper uses a buffer, reducing the number of
|
||||
//! calls and providing nicer methods for accessing exactly what you want.
|
||||
//!
|
||||
//! For example, [`BufReader`] works with the [`BufRead`] trait to add extra
|
||||
//! methods to any reader:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::BufReader;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let f = File::open("foo.txt")?;
|
||||
//! let mut reader = BufReader::new(f);
|
||||
//! let mut buffer = String::new();
|
||||
//!
|
||||
//! // read a line into buffer
|
||||
//! reader.read_line(&mut buffer)?;
|
||||
//!
|
||||
//! println!("{buffer}");
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [`BufWriter`] doesn't add any new ways of writing; it just buffers every call
|
||||
//! to [`write`][`Write::write`]:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::BufWriter;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let f = File::create("foo.txt")?;
|
||||
//! {
|
||||
//! let mut writer = BufWriter::new(f);
|
||||
//!
|
||||
//! // write a byte to the buffer
|
||||
//! writer.write(&[42])?;
|
||||
//!
|
||||
//! } // the buffer is flushed once writer goes out of scope
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Standard input and output
|
||||
//!
|
||||
//! A very common source of input is standard input:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let mut input = String::new();
|
||||
//!
|
||||
//! io::stdin().read_line(&mut input)?;
|
||||
//!
|
||||
//! println!("You typed: {}", input.trim());
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Note that you cannot use the [`?` operator] in functions that do not return
|
||||
//! a [`Result<T, E>`][`Result`]. Instead, you can call [`.unwrap()`]
|
||||
//! or `match` on the return value to catch any possible errors:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//!
|
||||
//! let mut input = String::new();
|
||||
//!
|
||||
//! io::stdin().read_line(&mut input).unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! And a very common source of output is standard output:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! io::stdout().write(&[42])?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Of course, using [`io::stdout`] directly is less common than something like
|
||||
//! [`println!`].
|
||||
//!
|
||||
//! ## Iterator types
|
||||
//!
|
||||
//! A large number of the structures provided by `std::io` are for various
|
||||
//! ways of iterating over I/O. For example, [`Lines`] is used to split over
|
||||
//! lines:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//! use std::io::prelude::*;
|
||||
//! use std::io::BufReader;
|
||||
//! use std::fs::File;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! let f = File::open("foo.txt")?;
|
||||
//! let reader = BufReader::new(f);
|
||||
//!
|
||||
//! for line in reader.lines() {
|
||||
//! println!("{}", line?);
|
||||
//! }
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Functions
|
||||
//!
|
||||
//! There are a number of [functions][functions-list] that offer access to various
|
||||
//! features. For example, we can use three of these functions to copy everything
|
||||
//! from standard input to standard output:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use std::io;
|
||||
//!
|
||||
//! fn main() -> io::Result<()> {
|
||||
//! io::copy(&mut io::stdin(), &mut io::stdout())?;
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! [functions-list]: #functions-1
|
||||
//!
|
||||
//! ## io::Result
|
||||
//!
|
||||
//! Last, but certainly not least, is [`io::Result`]. This type is used
|
||||
//! as the return type of many `std::io` functions that can cause an error, and
|
||||
//! can be returned from your own functions as well. Many of the examples in this
|
||||
//! module use the [`?` operator]:
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io;
|
||||
//!
|
||||
//! fn read_input() -> io::Result<()> {
|
||||
//! let mut input = String::new();
|
||||
//!
|
||||
//! io::stdin().read_line(&mut input)?;
|
||||
//!
|
||||
//! println!("You typed: {}", input.trim());
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The return type of `read_input()`, [`io::Result<()>`][`io::Result`], is a very
|
||||
//! common type for functions which don't have a 'real' return value, but do want to
|
||||
//! return errors if they happen. In this case, the only purpose of this function is
|
||||
//! to read the line and print it, so we use `()`.
|
||||
//!
|
||||
//! ## Platform-specific behavior
|
||||
//!
|
||||
//! Many I/O functions throughout the standard library are documented to indicate
|
||||
//! what various library or syscalls they are delegated to. This is done to help
|
||||
//! applications both understand what's happening under the hood as well as investigate
|
||||
//! any possibly unclear semantics. Note, however, that this is informative, not a binding
|
||||
//! contract. The implementation of many of these functions are subject to change over
|
||||
//! time and may call fewer or more syscalls/library functions.
|
||||
//!
|
||||
//! ## I/O Safety
|
||||
//!
|
||||
//! Rust follows an I/O safety discipline that is comparable to its memory safety discipline. This
|
||||
//! means that file descriptors can be *exclusively owned*. (Here, "file descriptor" is meant to
|
||||
//! subsume similar concepts that exist across a wide range of operating systems even if they might
|
||||
//! use a different name, such as "handle".) An exclusively owned file descriptor is one that no
|
||||
//! other code is allowed to access in any way, but the owner is allowed to access and even close
|
||||
//! it any time. A type that owns its file descriptor should usually close it in its `drop`
|
||||
//! function. Types like [`File`] own their file descriptor. Similarly, file descriptors
|
||||
//! can be *borrowed*, granting the temporary right to perform operations on this file descriptor.
|
||||
//! This indicates that the file descriptor will not be closed for the lifetime of the borrow, but
|
||||
//! it does *not* imply any right to close this file descriptor, since it will likely be owned by
|
||||
//! someone else.
|
||||
//!
|
||||
//! The platform-specific parts of the Rust standard library expose types that reflect these
|
||||
//! concepts, see [`os::unix`] and [`os::windows`].
|
||||
//!
|
||||
//! To uphold I/O safety, it is crucial that no code acts on file descriptors it does not own or
|
||||
//! borrow, and no code closes file descriptors it does not own. In other words, a safe function
|
||||
//! that takes a regular integer, treats it as a file descriptor, and acts on it, is *unsound*.
|
||||
//!
|
||||
//! Not upholding I/O safety and acting on a file descriptor without proof of ownership can lead to
|
||||
//! misbehavior and even Undefined Behavior in code that relies on ownership of its file
|
||||
//! descriptors: a closed file descriptor could be re-allocated, so the original owner of that file
|
||||
//! descriptor is now working on the wrong file. Some code might even rely on fully encapsulating
|
||||
//! its file descriptors with no operations being performed by any other part of the program.
|
||||
//!
|
||||
//! Note that exclusive ownership of a file descriptor does *not* imply exclusive ownership of the
|
||||
//! underlying kernel object that the file descriptor references (also called "open file description" on
|
||||
//! some operating systems). File descriptors basically work like [`Arc`]: when you receive an owned
|
||||
//! file descriptor, you cannot know whether there are any other file descriptors that reference the
|
||||
//! same kernel object. However, when you create a new kernel object, you know that you are holding
|
||||
//! the only reference to it. Just be careful not to lend it to anyone, since they can obtain a
|
||||
//! clone and then you can no longer know what the reference count is! In that sense, [`OwnedFd`] is
|
||||
//! like `Arc` and [`BorrowedFd<'a>`] is like `&'a Arc` (and similar for the Windows types). In
|
||||
//! particular, given a `BorrowedFd<'a>`, you are not allowed to close the file descriptor -- just
|
||||
//! like how, given a `&'a Arc`, you are not allowed to decrement the reference count and
|
||||
//! potentially free the underlying object. There is no equivalent to `Box` for file descriptors in
|
||||
//! the standard library (that would be a type that guarantees that the reference count is `1`),
|
||||
//! however, it would be possible for a crate to define a type with those semantics.
|
||||
//!
|
||||
//! [`File`]: crate::fs::File
|
||||
//! [`TcpStream`]: crate::net::TcpStream
|
||||
//! [`io::stdout`]: stdout
|
||||
//! [`io::Result`]: self::Result
|
||||
//! [`?` operator]: ../../book/appendix-02-operators.html
|
||||
//! [`Result`]: crate::result::Result
|
||||
//! [`.unwrap()`]: crate::result::Result::unwrap
|
||||
//! [`os::unix`]: ../os/unix/io/index.html
|
||||
//! [`os::windows`]: ../os/windows/io/index.html
|
||||
//! [`OwnedFd`]: ../os/fd/struct.OwnedFd.html
|
||||
//! [`BorrowedFd<'a>`]: ../os/fd/struct.BorrowedFd.html
|
||||
//! [`Arc`]: crate::sync::Arc
|
||||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
pub mod error;
|
||||
pub use self::buffered::{BufReader, BufWriter, IntoInnerError, LineWriter};
|
||||
pub use self::error::{Error, ErrorKind, Result, SimpleMessage, const_error};
|
||||
pub mod buffered;
|
||||
pub mod copy;
|
||||
pub mod cursor;
|
||||
pub mod impls;
|
||||
pub mod pipe;
|
||||
// pub mod stdio;
|
||||
pub mod prelude;
|
||||
pub mod util;
|
||||
|
||||
use crate::mem::{MaybeUninit, take};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::{cmp, fmt, slice, str, sys};
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
pub use core::io::{BorrowedBuf, BorrowedCursor};
|
||||
use core::slice::memchr;
|
||||
|
||||
use crate::fs::File;
|
||||
use io::IoBase;
|
||||
// pub use io::Read;
|
||||
// pub use io::Seek;
|
||||
// pub use io::SeekFrom;
|
||||
// pub use io::Write;
|
||||
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
|
||||
pub use self::buffered::WriterPanicked;
|
||||
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
|
||||
pub use self::error::RawOsError;
|
||||
#[doc(hidden)]
|
||||
#[unstable(feature = "io_const_error_internals", issue = "none")]
|
||||
pub use self::error::SimpleMessage;
|
||||
#[unstable(feature = "io_const_error", issue = "133448")]
|
||||
pub use self::error::const_error;
|
||||
#[doc(hidden)]
|
||||
#[doc(no_inline, hidden)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::{
|
||||
buffered::{BufReader, BufWriter, IntoInnerError, LineWriter},
|
||||
copy::copy,
|
||||
cursor::Cursor,
|
||||
error::{Error, ErrorKind, Result},
|
||||
util::{Empty, Repeat, Sink, empty, repeat, sink},
|
||||
};
|
||||
use crate::mem::{MaybeUninit, take};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::{cmp, fmt, slice, str, sys};
|
||||
|
||||
pub struct Stdin;
|
||||
|
||||
impl IoBase for Stdin {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl io::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
|
||||
}
|
||||
|
||||
// Part took from the real std
|
||||
mod buffered;
|
||||
pub(crate) mod copy;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod impls;
|
||||
pub mod prelude;
|
||||
mod util;
|
||||
|
||||
const DEFAULT_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
|
||||
|
||||
|
||||
struct Guard<'a> {
|
||||
buf: &'a mut Vec<u8>,
|
||||
len: usize,
|
||||
@@ -60,14 +346,30 @@ impl Drop for Guard<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// Several `read_to_string` and `read_line` methods in the standard library will
|
||||
// append data into a `String` buffer, but we need to be pretty careful when
|
||||
// doing this. The implementation will just call `.as_mut_vec()` and then
|
||||
// delegate to a byte-oriented reading method, but we must ensure that when
|
||||
// returning we never leave `buf` in a state such that it contains invalid UTF-8
|
||||
// in its bounds.
|
||||
//
|
||||
// To this end, we use an RAII guard (to protect against panics) which updates
|
||||
// the length of the string when it is dropped. This guard initially truncates
|
||||
// the string to the prior length and only after we've validated that the
|
||||
// new contents are valid UTF-8 do we allow it to set a longer length.
|
||||
//
|
||||
// The unsafety in this function is twofold:
|
||||
//
|
||||
// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8
|
||||
// checks.
|
||||
// 2. We're passing a raw buffer to the function `f`, and it is expected that
|
||||
// the function only *appends* bytes to the buffer. We'll get undefined
|
||||
// behavior if existing bytes are overwritten to have non-UTF-8 data.
|
||||
pub(crate) unsafe fn append_to_string<F>(buf: &mut String, f: F) -> Result<usize>
|
||||
where
|
||||
F: FnOnce(&mut Vec<u8>) -> Result<usize>,
|
||||
{
|
||||
let mut g = Guard {
|
||||
len: buf.len(),
|
||||
buf: unsafe { buf.as_mut_vec() },
|
||||
};
|
||||
let mut g = Guard { len: buf.len(), buf: unsafe { buf.as_mut_vec() } };
|
||||
let ret = f(g.buf);
|
||||
|
||||
// SAFETY: the caller promises to only append data to `buf`
|
||||
@@ -99,10 +401,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(
|
||||
// Optionally limit the maximum bytes read on each iteration.
|
||||
// This adds an arbitrary fiddle factor to allow for more data than we expect.
|
||||
let mut max_read_size = size_hint
|
||||
.and_then(|s| {
|
||||
s.checked_add(1024)?
|
||||
.checked_next_multiple_of(DEFAULT_BUF_SIZE)
|
||||
})
|
||||
.and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE))
|
||||
.unwrap_or(DEFAULT_BUF_SIZE);
|
||||
|
||||
let mut initialized = 0; // Extra initialized bytes from previous loop iteration
|
||||
@@ -243,10 +542,7 @@ pub(crate) fn default_read_vectored<F>(read: F, bufs: &mut [IoSliceMut<'_>]) ->
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<usize>,
|
||||
{
|
||||
let buf = bufs
|
||||
.iter_mut()
|
||||
.find(|b| !b.is_empty())
|
||||
.map_or(&mut [][..], |b| &mut **b);
|
||||
let buf = bufs.iter_mut().find(|b| !b.is_empty()).map_or(&mut [][..], |b| &mut **b);
|
||||
read(buf)
|
||||
}
|
||||
|
||||
@@ -254,10 +550,7 @@ pub(crate) fn default_write_vectored<F>(write: F, bufs: &[IoSlice<'_>]) -> Resul
|
||||
where
|
||||
F: FnOnce(&[u8]) -> Result<usize>,
|
||||
{
|
||||
let buf = bufs
|
||||
.iter()
|
||||
.find(|b| !b.is_empty())
|
||||
.map_or(&[][..], |b| &**b);
|
||||
let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b);
|
||||
write(buf)
|
||||
}
|
||||
|
||||
@@ -272,11 +565,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
if !buf.is_empty() {
|
||||
Err(Error::READ_EXACT_EOF)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
if !buf.is_empty() { Err(Error::READ_EXACT_EOF) } else { Ok(()) }
|
||||
}
|
||||
|
||||
pub(crate) fn default_read_buf<F>(read: F, mut cursor: BorrowedCursor<'_>) -> Result<()>
|
||||
@@ -331,10 +620,7 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
|
||||
}
|
||||
}
|
||||
|
||||
let mut output = Adapter {
|
||||
inner: this,
|
||||
error: Ok(()),
|
||||
};
|
||||
let mut output = Adapter { inner: this, error: Ok(()) };
|
||||
match fmt::write(&mut output, args) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(..) => {
|
||||
@@ -352,6 +638,79 @@ pub(crate) fn default_write_fmt<W: Write + ?Sized>(
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Read` trait allows for reading bytes from a source.
|
||||
///
|
||||
/// Implementors of the `Read` trait are called 'readers'.
|
||||
///
|
||||
/// Readers are defined by one required method, [`read()`]. Each call to [`read()`]
|
||||
/// will attempt to pull bytes from this source into a provided buffer. A
|
||||
/// number of other methods are implemented in terms of [`read()`], giving
|
||||
/// implementors a number of ways to read bytes while only needing to implement
|
||||
/// a single method.
|
||||
///
|
||||
/// Readers are intended to be composable with one another. Many implementors
|
||||
/// throughout [`std::io`] take and provide types which implement the `Read`
|
||||
/// trait.
|
||||
///
|
||||
/// Please note that each call to [`read()`] may involve a system call, and
|
||||
/// therefore, using something that implements [`BufRead`], such as
|
||||
/// [`BufReader`], will be more efficient.
|
||||
///
|
||||
/// Repeated calls to the reader use the same cursor, so for example
|
||||
/// calling `read_to_end` twice on a [`File`] will only return the file's
|
||||
/// contents once. It's recommended to first call `rewind()` in that case.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`]s implement `Read`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = File::open("foo.txt")?;
|
||||
/// let mut buffer = [0; 10];
|
||||
///
|
||||
/// // read up to 10 bytes
|
||||
/// f.read(&mut buffer)?;
|
||||
///
|
||||
/// let mut buffer = Vec::new();
|
||||
/// // read the whole file
|
||||
/// f.read_to_end(&mut buffer)?;
|
||||
///
|
||||
/// // read into a String, so that you don't need to do the conversion.
|
||||
/// let mut buffer = String::new();
|
||||
/// f.read_to_string(&mut buffer)?;
|
||||
///
|
||||
/// // and more! See the other methods for more details.
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Read from [`&str`] because [`&[u8]`][prim@slice] implements `Read`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::io;
|
||||
/// use std::io::prelude::*;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut b = "This string will be read".as_bytes();
|
||||
/// let mut buffer = [0; 10];
|
||||
///
|
||||
/// // read up to 10 bytes
|
||||
/// b.read(&mut buffer)?;
|
||||
///
|
||||
/// // etc... it works exactly as a File does!
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`read()`]: Read::read
|
||||
/// [`&str`]: prim@str
|
||||
/// [`std::io`]: self
|
||||
/// [`File`]: crate::fs::File
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[doc(notable_trait)]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "IoRead")]
|
||||
@@ -449,8 +808,7 @@ pub trait Read {
|
||||
/// buffer provided, or an empty one if none exists.
|
||||
#[stable(feature = "iovec", since = "1.36.0")]
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
||||
// default_read_vectored(|b| self.read(b), bufs)
|
||||
todo!()
|
||||
default_read_vectored(|b| self.read(b), bufs)
|
||||
}
|
||||
|
||||
/// Determines if this `Read`er has an efficient `read_vectored`
|
||||
@@ -560,8 +918,7 @@ pub trait Read {
|
||||
/// [`Vec::try_reserve`]: crate::vec::Vec::try_reserve
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
// default_read_to_end(self, buf, None)
|
||||
todo!()
|
||||
default_read_to_end(self, buf, None)
|
||||
}
|
||||
|
||||
/// Reads all bytes until EOF in this source, appending them to `buf`.
|
||||
@@ -617,8 +974,7 @@ pub trait Read {
|
||||
/// [`std::fs::read_to_string`]: crate::fs::read_to_string
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
|
||||
// default_read_to_string(self, buf, None)
|
||||
todo!()
|
||||
default_read_to_string(self, buf, None)
|
||||
}
|
||||
|
||||
/// Reads the exact number of bytes required to fill `buf`.
|
||||
@@ -671,8 +1027,7 @@ pub trait Read {
|
||||
/// ```
|
||||
#[stable(feature = "read_exact", since = "1.6.0")]
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
|
||||
// default_read_exact(self, buf)
|
||||
todo!()
|
||||
default_read_exact(self, buf)
|
||||
}
|
||||
|
||||
/// Pull some bytes from this source into the specified buffer.
|
||||
@@ -685,8 +1040,7 @@ pub trait Read {
|
||||
/// This method makes it possible to return both data and an error but it is advised against.
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
|
||||
// default_read_buf(|b| self.read(b), buf)
|
||||
todo!()
|
||||
default_read_buf(|b| self.read(b), buf)
|
||||
}
|
||||
|
||||
/// Reads the exact number of bytes required to fill `cursor`.
|
||||
@@ -709,8 +1063,7 @@ pub trait Read {
|
||||
/// If this function returns an error, all bytes read will be appended to `cursor`.
|
||||
#[unstable(feature = "read_buf", issue = "78485")]
|
||||
fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
|
||||
// default_read_buf_exact(self, cursor)
|
||||
todo!()
|
||||
default_read_buf_exact(self, cursor)
|
||||
}
|
||||
|
||||
/// Creates a "by reference" adapter for this instance of `Read`.
|
||||
@@ -833,11 +1186,7 @@ pub trait Read {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Chain {
|
||||
first: self,
|
||||
second: next,
|
||||
done_first: false,
|
||||
}
|
||||
Chain { first: self, second: next, done_first: false }
|
||||
}
|
||||
|
||||
/// Creates an adapter which will read at most `limit` bytes from it.
|
||||
@@ -876,11 +1225,7 @@ pub trait Read {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Take {
|
||||
inner: self,
|
||||
len: limit,
|
||||
limit,
|
||||
}
|
||||
Take { inner: self, len: limit, limit }
|
||||
}
|
||||
|
||||
/// Read and return a fixed array of bytes from this source.
|
||||
@@ -1934,6 +2279,55 @@ fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A `BufRead` is a type of `Read`er which has an internal buffer, allowing it
|
||||
/// to perform extra ways of reading.
|
||||
///
|
||||
/// For example, reading line-by-line is inefficient without using a buffer, so
|
||||
/// if you want to read by line, you'll need `BufRead`, which includes a
|
||||
/// [`read_line`] method as well as a [`lines`] iterator.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A locked standard input implements `BufRead`:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// use std::io::prelude::*;
|
||||
///
|
||||
/// let stdin = io::stdin();
|
||||
/// for line in stdin.lock().lines() {
|
||||
/// println!("{}", line?);
|
||||
/// }
|
||||
/// # std::io::Result::Ok(())
|
||||
/// ```
|
||||
///
|
||||
/// If you have something that implements [`Read`], you can use the [`BufReader`
|
||||
/// type][`BufReader`] to turn it into a `BufRead`.
|
||||
///
|
||||
/// For example, [`File`] implements [`Read`], but not `BufRead`.
|
||||
/// [`BufReader`] to the rescue!
|
||||
///
|
||||
/// [`File`]: crate::fs::File
|
||||
/// [`read_line`]: BufRead::read_line
|
||||
/// [`lines`]: BufRead::lines
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::io::{self, BufReader};
|
||||
/// use std::io::prelude::*;
|
||||
/// use std::fs::File;
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let f = File::open("foo.txt")?;
|
||||
/// let f = BufReader::new(f);
|
||||
///
|
||||
/// for line in f.lines() {
|
||||
/// let line = line?;
|
||||
/// println!("{line}");
|
||||
/// }
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "IoBufRead")]
|
||||
pub trait BufRead: Read {
|
||||
@@ -2257,10 +2651,7 @@ pub trait BufRead: Read {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Split {
|
||||
buf: self,
|
||||
delim: byte,
|
||||
}
|
||||
Split { buf: self, delim: byte }
|
||||
}
|
||||
|
||||
/// Returns an iterator over the lines of this reader.
|
||||
@@ -2300,6 +2691,7 @@ pub trait BufRead: Read {
|
||||
Lines { buf: self }
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter to chain together two readers.
|
||||
///
|
||||
/// This struct is generally created by calling [`chain`] on a reader.
|
||||
@@ -2465,11 +2857,7 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
if !self.done_first {
|
||||
self.first.consume(amt)
|
||||
} else {
|
||||
self.second.consume(amt)
|
||||
}
|
||||
if !self.done_first { self.first.consume(amt) } else { self.second.consume(amt) }
|
||||
}
|
||||
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
@@ -2499,15 +2887,13 @@ impl<T, U> SizeHint for Chain<T, U> {
|
||||
|
||||
#[inline]
|
||||
fn upper_bound(&self) -> Option<usize> {
|
||||
match (
|
||||
SizeHint::upper_bound(&self.first),
|
||||
SizeHint::upper_bound(&self.second),
|
||||
) {
|
||||
match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
|
||||
(Some(first), Some(second)) => first.checked_add(second),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader adapter which limits the bytes read from an underlying reader.
|
||||
///
|
||||
/// This struct is generally created by calling [`take`] on a reader.
|
||||
@@ -2790,11 +3176,7 @@ impl<T: Seek> Seek for Take<T> {
|
||||
self.limit = self.limit.wrapping_sub(offset as u64);
|
||||
break;
|
||||
}
|
||||
let offset = if new_position > self.position() {
|
||||
i64::MAX
|
||||
} else {
|
||||
i64::MIN
|
||||
};
|
||||
let offset = if new_position > self.position() { i64::MAX } else { i64::MIN };
|
||||
self.inner.seek_relative(offset)?;
|
||||
self.limit = self.limit.wrapping_sub(offset as u64);
|
||||
}
|
||||
@@ -2810,11 +3192,7 @@ impl<T: Seek> Seek for Take<T> {
|
||||
}
|
||||
|
||||
fn seek_relative(&mut self, offset: i64) -> Result<()> {
|
||||
if !self
|
||||
.position()
|
||||
.checked_add_signed(offset)
|
||||
.is_some_and(|p| p <= self.len)
|
||||
{
|
||||
if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) {
|
||||
return Err(ErrorKind::InvalidInput.into());
|
||||
}
|
||||
self.inner.seek_relative(offset)?;
|
||||
@@ -1,7 +1,5 @@
|
||||
#![allow(missing_copy_implementations)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::io::{
|
||||
@@ -1,299 +1,70 @@
|
||||
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
||||
pub mod error;
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(not(feature = "std"), stable(feature = "rust1", since = "1.0.0"))]
|
||||
#![cfg_attr(
|
||||
not(feature = "std"),
|
||||
allow(
|
||||
internal_features,
|
||||
incomplete_features,
|
||||
clippy::all,
|
||||
dead_code,
|
||||
unused_imports
|
||||
)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
not(feature = "std"),
|
||||
feature(
|
||||
fmt_internals,
|
||||
rustc_attrs,
|
||||
decl_macro,
|
||||
allow_internal_unstable,
|
||||
staged_api,
|
||||
core_io_borrowed_buf,
|
||||
specialization,
|
||||
prelude_import,
|
||||
allocator_api,
|
||||
slice_internals,
|
||||
doc_notable_trait,
|
||||
rustdoc_internals,
|
||||
io_const_error,
|
||||
maybe_uninit_array_assume_init,
|
||||
try_with_capacity,
|
||||
maybe_uninit_fill
|
||||
)
|
||||
)]
|
||||
|
||||
use error::IoError;
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub mod io;
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub(crate) mod sys;
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use io::*;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod std_prelude {
|
||||
pub(crate) use core::prelude::rust_2024::*;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Provides IO error as an associated type.
|
||||
///
|
||||
/// Must be implemented for all types that also implement at least one of the following traits: `Read`, `Write`,
|
||||
/// `Seek`.
|
||||
pub trait IoBase {
|
||||
/// Type of errors returned by input/output operations.
|
||||
type Error: IoError;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use super::alloc_crate::{
|
||||
boxed::Box,
|
||||
string::{String, ToString},
|
||||
vec,
|
||||
vec::Vec,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[prelude_import]
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use std_prelude::*;
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate alloc as alloc_crate;
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub(crate) use alloc_crate::alloc;
|
||||
|
||||
/// The `Read` trait allows for reading bytes from a source.
|
||||
///
|
||||
/// It is based on the `std::io::Read` trait.
|
||||
pub trait Read: IoBase {
|
||||
/// Pull some bytes from this source into the specified buffer, returning how many bytes were read.
|
||||
///
|
||||
/// This function does not provide any guarantees about whether it blocks waiting for data, but if an object needs
|
||||
/// to block for a read and cannot, it will typically signal this via an Err return value.
|
||||
///
|
||||
/// If the return value of this method is `Ok(n)`, then it must be guaranteed that `0 <= n <= buf.len()`. A nonzero
|
||||
/// `n` value indicates that the buffer buf has been filled in with n bytes of data from this source. If `n` is
|
||||
/// `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. This reader has reached its "end of file" and will likely no longer be able to produce bytes. Note that this
|
||||
/// does not mean that the reader will always no longer be able to produce bytes.
|
||||
/// 2. The buffer specified was 0 bytes in length.
|
||||
///
|
||||
/// It is not an error if the returned value `n` is smaller than the buffer size, even when the reader is not at
|
||||
/// the end of the stream yet. This may happen for example because fewer bytes are actually available right now
|
||||
/// (e. g. being close to end-of-file) or because `read()` was interrupted by a signal.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If this function encounters any form of I/O or other error, an error will be returned. If an error is returned
|
||||
/// then it must be guaranteed that no bytes were read.
|
||||
/// An error for which `IoError::is_interrupted` returns true is non-fatal and the read operation should be retried
|
||||
/// if there is nothing else to do.
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc_crate::collections;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use core::{cmp, error, fmt, hint, mem, ops, ptr, result, slice, str};
|
||||
|
||||
/// Read the exact number of bytes required to fill `buf`.
|
||||
///
|
||||
/// This function reads as many bytes as necessary to completely fill the specified buffer `buf`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If this function encounters an error for which `IoError::is_interrupted` returns true then the error is ignored
|
||||
/// and the operation will continue.
|
||||
///
|
||||
/// If this function encounters an end of file before completely filling the buffer, it returns an error
|
||||
/// instantiated by a call to `IoError::new_unexpected_eof_error`. The contents of `buf` are unspecified in this
|
||||
/// case.
|
||||
///
|
||||
/// If this function returns an error, it is unspecified how many bytes it has read, but it will never read more
|
||||
/// than would be necessary to completely fill the buffer.
|
||||
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
|
||||
while !buf.is_empty() {
|
||||
match self.read(buf) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => {
|
||||
let tmp = buf;
|
||||
buf = &mut tmp[n..];
|
||||
}
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
if buf.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Self::Error::new_unexpected_eof_error())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Self::Error> {
|
||||
const CHUNK_SIZE: usize = 32;
|
||||
let start_len = buf.len();
|
||||
loop {
|
||||
let mut chunk_buf = [0; CHUNK_SIZE];
|
||||
let read = self.read(&mut chunk_buf)?;
|
||||
buf.extend_from_slice(&chunk_buf[..read]);
|
||||
|
||||
if read == 0 {
|
||||
return Ok(buf.len() - start_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Self::Error> {
|
||||
let read = self.read_to_end(unsafe { buf.as_mut_vec() })?;
|
||||
|
||||
if str::from_utf8(buf.as_bytes()).is_err() {
|
||||
Err(Self::Error::new_invalid_utf8_error())
|
||||
} else {
|
||||
Ok(read)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Write` trait allows for writing bytes into the sink.
|
||||
///
|
||||
/// It is based on the `std::io::Write` trait.
|
||||
pub trait Write: IoBase {
|
||||
/// Write a buffer into this writer, returning how many bytes were written.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Each call to write may generate an I/O error indicating that the operation could not be completed. If an error
|
||||
/// is returned then no bytes in the buffer were written to this writer.
|
||||
/// It is not considered an error if the entire buffer could not be written to this writer.
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;
|
||||
|
||||
/// Attempts to write an entire buffer into this writer.
|
||||
///
|
||||
/// This method will continuously call `write` until there is no more data to be written or an error is returned.
|
||||
/// Errors for which `IoError::is_interrupted` method returns true are being skipped. This method will not return
|
||||
/// until the entire buffer has been successfully written or such an error occurs.
|
||||
/// If `write` returns 0 before the entire buffer has been written this method will return an error instantiated by
|
||||
/// a call to `IoError::new_write_zero_error`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return the first error for which `IoError::is_interrupted` method returns false that `write`
|
||||
/// returns.
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::Error> {
|
||||
while !buf.is_empty() {
|
||||
match self.write(buf) {
|
||||
Ok(0) => {
|
||||
return Err(Self::Error::new_write_zero_error());
|
||||
}
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Flush this output stream, ensuring that all intermediately buffered contents reach their destination.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// It is considered an error if not all bytes could be written due to I/O errors or EOF being reached.
|
||||
fn flush(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Enumeration of possible methods to seek within an I/O object.
|
||||
///
|
||||
/// It is based on the `std::io::SeekFrom` enum.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SeekFrom {
|
||||
/// Sets the offset to the provided number of bytes.
|
||||
Start(u64),
|
||||
/// Sets the offset to the size of this object plus the specified number of bytes.
|
||||
End(i64),
|
||||
/// Sets the offset to the current position plus the specified number of bytes.
|
||||
Current(i64),
|
||||
}
|
||||
|
||||
/// The `Seek` trait provides a cursor which can be moved within a stream of bytes.
|
||||
///
|
||||
/// It is based on the `std::io::Seek` trait.
|
||||
pub trait Seek: IoBase {
|
||||
/// Seek to an offset, in bytes, in a stream.
|
||||
///
|
||||
/// A seek beyond the end of a stream or to a negative position is not allowed.
|
||||
///
|
||||
/// If the seek operation completed successfully, this method returns the new position from the start of the
|
||||
/// stream. That position can be used later with `SeekFrom::Start`.
|
||||
///
|
||||
/// # Errors
|
||||
/// Seeking to a negative offset is considered an error.
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error>;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl From<SeekFrom> for std::io::SeekFrom {
|
||||
fn from(from: SeekFrom) -> Self {
|
||||
match from {
|
||||
SeekFrom::Start(n) => std::io::SeekFrom::Start(n),
|
||||
SeekFrom::End(n) => std::io::SeekFrom::End(n),
|
||||
SeekFrom::Current(n) => std::io::SeekFrom::Current(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl From<std::io::SeekFrom> for SeekFrom {
|
||||
fn from(from: std::io::SeekFrom) -> Self {
|
||||
match from {
|
||||
std::io::SeekFrom::Start(n) => SeekFrom::Start(n),
|
||||
std::io::SeekFrom::End(n) => SeekFrom::End(n),
|
||||
std::io::SeekFrom::Current(n) => SeekFrom::Current(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl IoBase for std::fs::File {
|
||||
type Error = std::io::Error;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl<T: std::io::Read + IoBase<Error = std::io::Error>> Read for T {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.read(buf).map_err(Error::Io)
|
||||
}
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read_exact(buf).map_err(Error::Io)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl<T: std::io::Write + IoBase<Error = std::io::Error>> Write for T {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.write(buf).map_err(Error::Io)
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write_all(buf).map_err(Error::Io)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.flush().map_err(Error::Io)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl<T: std::io::Seek + IoBase<Error = std::io::Error>> Seek for T {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
|
||||
self.seek(pos.into()).map_err(Error::Io)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReadLeExt {
|
||||
type Error;
|
||||
fn read_u8(&mut self) -> Result<u8, Self::Error>;
|
||||
fn read_u16_le(&mut self) -> Result<u16, Self::Error>;
|
||||
fn read_u32_le(&mut self) -> Result<u32, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T: Read> ReadLeExt for T {
|
||||
type Error = <Self as IoBase>::Error;
|
||||
|
||||
fn read_u8(&mut self) -> Result<u8, Self::Error> {
|
||||
let mut buf = [0_u8; 1];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
|
||||
fn read_u16_le(&mut self) -> Result<u16, Self::Error> {
|
||||
let mut buf = [0_u8; 2];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
fn read_u32_le(&mut self) -> Result<u32, Self::Error> {
|
||||
let mut buf = [0_u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) trait WriteLeExt {
|
||||
type Error;
|
||||
fn write_u8(&mut self, n: u8) -> Result<(), Self::Error>;
|
||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error>;
|
||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
impl<T: Write> WriteLeExt for T {
|
||||
type Error = <Self as IoBase>::Error;
|
||||
|
||||
fn write_u8(&mut self, n: u8) -> Result<(), Self::Error> {
|
||||
self.write_all(&[n])
|
||||
}
|
||||
|
||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error> {
|
||||
self.write_all(&n.to_le_bytes())
|
||||
}
|
||||
|
||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error> {
|
||||
self.write_all(&n.to_le_bytes())
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::io::*;
|
||||
|
||||
1
crates/io/src/sys.rs
Normal file
1
crates/io/src/sys.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub(crate) mod io;
|
||||
4
crates/io/src/sys/io/error/mod.rs
Normal file
4
crates/io/src/sys/io/error/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
mod generic;
|
||||
pub use generic::*;
|
||||
|
||||
pub type RawOsError = i32;
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::mem;
|
||||
use core::mem;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct IoSlice<'a>(&'a [u8]);
|
||||
@@ -1,15 +1,10 @@
|
||||
|
||||
pub enum CopyState {
|
||||
#[cfg_attr(not(any(target_os = "linux", target_os = "android")), expect(dead_code))]
|
||||
Ended(u64),
|
||||
Fallback(u64),
|
||||
}
|
||||
|
||||
cfg_select! {
|
||||
any(target_os = "linux", target_os = "android") => {
|
||||
mod linux;
|
||||
pub use linux::kernel_copy;
|
||||
}
|
||||
_ => {
|
||||
use crate::io::{Result, Read, Write};
|
||||
|
||||
pub fn kernel_copy<R: ?Sized, W: ?Sized>(_reader: &mut R, _writer: &mut W) -> Result<CopyState>
|
||||
@@ -19,5 +14,3 @@ cfg_select! {
|
||||
{
|
||||
Ok(CopyState::Fallback(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
26
crates/io/src/sys/io/mod.rs
Normal file
26
crates/io/src/sys/io/mod.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
mod error;
|
||||
|
||||
mod io_slice {
|
||||
mod unsupported;
|
||||
pub use unsupported::*;
|
||||
}
|
||||
|
||||
mod is_terminal {
|
||||
mod unsupported;
|
||||
pub use unsupported::*;
|
||||
}
|
||||
|
||||
mod kernel_copy;
|
||||
|
||||
pub use error::{RawOsError, decode_error_kind, errno, error_string, is_interrupted};
|
||||
pub use io_slice::{IoSlice, IoSliceMut};
|
||||
pub use is_terminal::is_terminal;
|
||||
pub use kernel_copy::{CopyState, kernel_copy};
|
||||
|
||||
// Bare metal platforms usually have very small amounts of RAM
|
||||
// (in the order of hundreds of KB)
|
||||
pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") {
|
||||
512
|
||||
} else {
|
||||
8 * 1024
|
||||
};
|
||||
@@ -7,6 +7,6 @@ edition = "2024"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
image = "0.25"
|
||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||
syn = { version = "2", features = ["full"] }
|
||||
zyn = "0.5"
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
[package]
|
||||
name = "os-std-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
@@ -5,7 +5,7 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
bffs = { path = "../bffs" }
|
||||
io = { path = "../io" }
|
||||
io = { package = "no-std-io", path = "../io" }
|
||||
|
||||
[features]
|
||||
kernel = []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use io::{IoBase, Read, Write};
|
||||
use io::{Read, Write};
|
||||
|
||||
use crate::syscall;
|
||||
|
||||
@@ -19,22 +19,18 @@ impl File {
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for File {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for File {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
Ok(syscall::read(self.as_fd(), buf) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for File {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
|
||||
Ok(syscall::write(self.as_fd(), buf) as usize)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
use core::{alloc::Layout, time::Duration};
|
||||
|
||||
use bffs::path::Path;
|
||||
use io::SeekFrom;
|
||||
|
||||
use crate::fs::File;
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum SysCall {
|
||||
Read = 0,
|
||||
@@ -18,8 +13,7 @@ pub enum SysCall {
|
||||
ExecVE = 59,
|
||||
Exit = 60,
|
||||
NanoSleep = 101,
|
||||
WriteIntTemp = 998,
|
||||
WriteTemp = 999,
|
||||
WaitPid = 247,
|
||||
Unimplemented = 1 << 31,
|
||||
}
|
||||
|
||||
@@ -37,8 +31,7 @@ impl From<u64> for SysCall {
|
||||
59 => SysCall::ExecVE,
|
||||
60 => SysCall::Exit,
|
||||
101 => SysCall::NanoSleep,
|
||||
998 => SysCall::WriteIntTemp,
|
||||
999 => SysCall::WriteTemp,
|
||||
247 => SysCall::WaitPid,
|
||||
_ => SysCall::Unimplemented,
|
||||
}
|
||||
}
|
||||
@@ -100,10 +93,12 @@ macro_rules! syscall {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn exit() {
|
||||
pub fn exit() -> ! {
|
||||
unsafe {
|
||||
syscall!(SysCall::Exit);
|
||||
}
|
||||
#[allow(clippy::empty_loop)]
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub fn sleep(duration: Duration) {
|
||||
@@ -113,21 +108,8 @@ pub fn sleep(duration: Duration) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_string_temp(content: &str) {
|
||||
unsafe {
|
||||
syscall!(
|
||||
SysCall::WriteTemp,
|
||||
content.as_ptr() as u64,
|
||||
content.len() as u64
|
||||
);
|
||||
}
|
||||
}
|
||||
pub fn write_int_temp(content: u64) {
|
||||
unsafe {
|
||||
syscall!(SysCall::WriteIntTemp, content);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(fuzzy_provenance_casts)]
|
||||
pub fn alloc(layout: Layout) -> *mut u8 {
|
||||
unsafe {
|
||||
let size = layout.size();
|
||||
@@ -143,13 +125,12 @@ pub fn dealloc(ptr: *mut u8, layout: core::alloc::Layout) {
|
||||
syscall!(SysCall::Dealloc, ptr as u64, size as u64, align as u64);
|
||||
}
|
||||
}
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> File {
|
||||
pub fn open(path: &str) -> u64 {
|
||||
unsafe {
|
||||
let path_str = path.as_ref().as_str();
|
||||
let ptr = path_str.as_ptr();
|
||||
let size = path_str.len();
|
||||
let ptr = path.as_ptr();
|
||||
let size = path.len();
|
||||
let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64);
|
||||
File::from_raw_fd(fd)
|
||||
fd
|
||||
}
|
||||
}
|
||||
pub fn close(file_descriptor: u64) {
|
||||
@@ -173,29 +154,36 @@ pub fn read(file_descriptor: u64, buf: &mut [u8]) -> u64 {
|
||||
len
|
||||
}
|
||||
}
|
||||
pub fn seek(file_descriptor: u64, seek: SeekFrom) {
|
||||
/// seek_type: 0 -> start, 1 -> end, 2 -> current
|
||||
pub fn seek(file_descriptor: u64, seek_type: u8, seek: u64) {
|
||||
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_descriptor, discriminant, value);
|
||||
syscall!(SysCall::Seek, file_descriptor, seek_type as u64, seek);
|
||||
}
|
||||
}
|
||||
pub fn spawn<P: AsRef<Path>>(path: P) {
|
||||
pub fn spawn(path: &str, argc: isize, argv: *const *const u8) -> u64 {
|
||||
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);
|
||||
let ptr = path.as_ptr();
|
||||
let size = path.len();
|
||||
let (pid, ..) = syscall!(
|
||||
SysCall::Spawn,
|
||||
ptr as u64,
|
||||
size as u64,
|
||||
argc as u64,
|
||||
argv as u64
|
||||
);
|
||||
pid
|
||||
}
|
||||
}
|
||||
pub fn execve<P: AsRef<Path>>(path: P) {
|
||||
pub fn execve(path: &str) {
|
||||
unsafe {
|
||||
let path_str = path.as_ref().as_str();
|
||||
let ptr = path_str.as_ptr();
|
||||
let size = path_str.len();
|
||||
let ptr = path.as_ptr();
|
||||
let size = path.len();
|
||||
syscall!(SysCall::ExecVE, ptr as u64, size as u64);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize) {
|
||||
unsafe {
|
||||
syscall!(SysCall::WaitPid, pid as u64);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
[package]
|
||||
name = "std"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
os-std-macros = { path = "../os-std-macros" }
|
||||
shared = { path = "../shared", features = ["user"] }
|
||||
io = { path = "../io", features = ["alloc"] }
|
||||
@@ -1,490 +0,0 @@
|
||||
//! 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
//! The `ByteStr` and `ByteString` types and trait implementations.
|
||||
|
||||
#[unstable(feature = "bstr", issue = "134915")]
|
||||
pub use alloc_crate::bstr::{ByteStr, ByteString};
|
||||
@@ -1,4 +0,0 @@
|
||||
#[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};
|
||||
@@ -1,14 +0,0 @@
|
||||
//! [`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;
|
||||
@@ -1,207 +0,0 @@
|
||||
//! 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;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,311 +0,0 @@
|
||||
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
3532
crates/std/src/fs.rs
File diff suppressed because it is too large
Load Diff
@@ -1,91 +0,0 @@
|
||||
//! 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};
|
||||
@@ -1,159 +0,0 @@
|
||||
//! 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()
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
use crate::cmp::{max, min};
|
||||
use alloc_crate::collections::VecDeque;
|
||||
use crate::io;
|
||||
use crate::io::*;
|
||||
|
||||
#[test]
|
||||
fn copy_copies() {
|
||||
let mut r = repeat(0).take(4);
|
||||
let mut w = sink();
|
||||
assert_eq!(copy(&mut r, &mut w).unwrap(), 4);
|
||||
|
||||
let mut r = repeat(0).take(1 << 17);
|
||||
assert_eq!(copy(&mut r as &mut dyn Read, &mut w as &mut dyn Write).unwrap(), 1 << 17);
|
||||
}
|
||||
|
||||
struct ShortReader {
|
||||
cap: usize,
|
||||
read_size: usize,
|
||||
observed_buffer: usize,
|
||||
}
|
||||
|
||||
impl Read for ShortReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let bytes = min(self.cap, self.read_size).min(buf.len());
|
||||
self.cap -= bytes;
|
||||
self.observed_buffer = max(self.observed_buffer, buf.len());
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
struct WriteObserver {
|
||||
observed_buffer: usize,
|
||||
}
|
||||
|
||||
impl Write for WriteObserver {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.observed_buffer = max(self.observed_buffer, buf.len());
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_bufwriter() {
|
||||
let cap = 117 * 1024;
|
||||
let buf_sz = 16 * 1024;
|
||||
let mut r = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
|
||||
let mut w = BufWriter::with_capacity(buf_sz, WriteObserver { observed_buffer: 0 });
|
||||
assert_eq!(
|
||||
copy(&mut r, &mut w).unwrap(),
|
||||
cap as u64,
|
||||
"expected the whole capacity to be copied"
|
||||
);
|
||||
assert_eq!(r.observed_buffer, buf_sz, "expected a large buffer to be provided to the reader");
|
||||
assert!(w.get_mut().observed_buffer > DEFAULT_BUF_SIZE, "expected coalesced writes");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_bufreader() {
|
||||
let mut source = vec![0; 768 * 1024];
|
||||
source[1] = 42;
|
||||
let mut buffered = BufReader::with_capacity(256 * 1024, Cursor::new(&mut source));
|
||||
|
||||
let mut sink = Vec::new();
|
||||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
|
||||
assert_eq!(source.as_slice(), sink.as_slice());
|
||||
|
||||
let buf_sz = 71 * 1024;
|
||||
assert!(buf_sz > DEFAULT_BUF_SIZE, "test precondition");
|
||||
|
||||
let mut buffered = BufReader::with_capacity(buf_sz, Cursor::new(&mut source));
|
||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
||||
assert_eq!(crate::io::copy(&mut buffered, &mut sink).unwrap(), source.len() as u64);
|
||||
assert_eq!(
|
||||
sink.observed_buffer, buf_sz,
|
||||
"expected a large buffer to be provided to the writer"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_to_vec() {
|
||||
let cap = DEFAULT_BUF_SIZE * 10;
|
||||
let mut source = ShortReader { cap, observed_buffer: 0, read_size: DEFAULT_BUF_SIZE };
|
||||
let mut sink = Vec::new();
|
||||
let copied = io::copy(&mut source, &mut sink).unwrap();
|
||||
assert_eq!(cap as u64, copied);
|
||||
assert_eq!(sink.len() as u64, copied);
|
||||
assert!(
|
||||
source.observed_buffer > DEFAULT_BUF_SIZE,
|
||||
"expected a large buffer to be provided to the reader, got {}",
|
||||
source.observed_buffer
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_from_vecdeque() {
|
||||
let mut source = VecDeque::with_capacity(100 * 1024);
|
||||
for _ in 0..20 * 1024 {
|
||||
source.push_front(0);
|
||||
}
|
||||
for _ in 0..20 * 1024 {
|
||||
source.push_back(0);
|
||||
}
|
||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
||||
assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
|
||||
assert_eq!(20 * 1024, sink.observed_buffer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_specializes_from_slice() {
|
||||
let mut source = [1; 60 * 1024].as_slice();
|
||||
let mut sink = WriteObserver { observed_buffer: 0 };
|
||||
assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
|
||||
assert_eq!(60 * 1024, sink.observed_buffer);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod io_benches {
|
||||
use test::Bencher;
|
||||
|
||||
use crate::fs::{File, OpenOptions};
|
||||
use crate::io::BufReader;
|
||||
use crate::io::prelude::*;
|
||||
|
||||
#[bench]
|
||||
#[cfg_attr(target_os = "emscripten", ignore)] // no /dev
|
||||
fn bench_copy_buf_reader(b: &mut Bencher) {
|
||||
let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed");
|
||||
// use dyn to avoid specializations unrelated to readbuf
|
||||
let dyn_in = &mut file_in as &mut dyn Read;
|
||||
let mut reader = BufReader::with_capacity(256 * 1024, dyn_in.take(0));
|
||||
let mut writer =
|
||||
OpenOptions::new().write(true).open("/dev/null").expect("opening /dev/null failed");
|
||||
|
||||
const BYTES: u64 = 1024 * 1024;
|
||||
|
||||
b.bytes = BYTES;
|
||||
|
||||
b.iter(|| {
|
||||
reader.get_mut().set_limit(BYTES);
|
||||
crate::io::copy(&mut reader, &mut writer).unwrap()
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,567 +0,0 @@
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{Cursor, IoSlice, IoSliceMut, SeekFrom};
|
||||
|
||||
#[test]
|
||||
fn test_vec_writer() {
|
||||
let mut writer = Vec::new();
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(writer, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_writer() {
|
||||
let mut writer = Cursor::new(Vec::new());
|
||||
writer.set_position(10);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..10], &[0; 10]);
|
||||
assert_eq!(&writer.get_ref()[10..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_writer_preallocated() {
|
||||
let mut writer = Cursor::new(vec![0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 10]);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_mut_writer() {
|
||||
let mut vec = Vec::new();
|
||||
let mut writer = Cursor::new(&mut vec);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(
|
||||
writer
|
||||
.write_vectored(&[IoSlice::new(&[]), IoSlice::new(&[8, 9]), IoSlice::new(&[10])],)
|
||||
.unwrap(),
|
||||
3
|
||||
);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
fn test_slice_writer<T>(writer: &mut Cursor<T>)
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
Cursor<T>: Write,
|
||||
{
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(writer.position(), 8);
|
||||
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||
assert_eq!(writer.position(), 8);
|
||||
|
||||
assert_eq!(writer.write(&[8, 9]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[10]).unwrap(), 0);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
assert_eq!(writer.get_ref().as_ref(), b);
|
||||
}
|
||||
|
||||
fn test_slice_writer_vectored<T>(writer: &mut Cursor<T>)
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
Cursor<T>: Write,
|
||||
{
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(
|
||||
writer.write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7]),]).unwrap(),
|
||||
7,
|
||||
);
|
||||
assert_eq!(writer.position(), 8);
|
||||
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||
assert_eq!(writer.position(), 8);
|
||||
|
||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1);
|
||||
assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
assert_eq!(writer.get_ref().as_ref(), b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_box_slice_writer() {
|
||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||
test_slice_writer(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_box_slice_writer_vectored() {
|
||||
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice());
|
||||
test_slice_writer_vectored(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_writer() {
|
||||
let mut writer = Cursor::new([0u8; 9]);
|
||||
test_slice_writer(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_writer_vectored() {
|
||||
let mut writer = Cursor::new([0u8; 9]);
|
||||
test_slice_writer_vectored(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer() {
|
||||
let mut buf = [0 as u8; 9];
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
test_slice_writer(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_vectored() {
|
||||
let mut buf = [0 as u8; 9];
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
test_slice_writer_vectored(&mut writer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_seek() {
|
||||
let mut buf = [0 as u8; 8];
|
||||
{
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2);
|
||||
assert_eq!(writer.position(), 2);
|
||||
assert_eq!(writer.write(&[2]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 3);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[3]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 2);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
||||
assert_eq!(writer.position(), 7);
|
||||
assert_eq!(writer.write(&[4]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 8);
|
||||
}
|
||||
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
|
||||
assert_eq!(buf, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_error() {
|
||||
let mut buf = [0 as u8; 2];
|
||||
let mut writer = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[0, 0]).unwrap(), 1);
|
||||
assert_eq!(writer.write(&[0, 0]).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_reader() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_reader_vectored() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(
|
||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||
1,
|
||||
);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf1 = [0; 4];
|
||||
let mut buf2 = [0; 4];
|
||||
assert_eq!(
|
||||
reader
|
||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2),])
|
||||
.unwrap(),
|
||||
7,
|
||||
);
|
||||
let b1: &[_] = &[1, 2, 3, 4];
|
||||
let b2: &[_] = &[5, 6, 7];
|
||||
assert_eq!(buf1, b1);
|
||||
assert_eq!(&buf2[..3], b2);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boxed_slice_reader() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boxed_slice_reader_vectored() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7].into_boxed_slice());
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(
|
||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||
1,
|
||||
);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf1 = [0; 4];
|
||||
let mut buf2 = [0; 4];
|
||||
assert_eq!(
|
||||
reader
|
||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||
.unwrap(),
|
||||
7,
|
||||
);
|
||||
let b1: &[_] = &[1, 2, 3, 4];
|
||||
let b2: &[_] = &[5, 6, 7];
|
||||
assert_eq!(buf1, b1);
|
||||
assert_eq!(&buf2[..3], b2);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end() {
|
||||
let mut reader = Cursor::new(vec![0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
let mut v = Vec::new();
|
||||
reader.read_to_end(&mut v).unwrap();
|
||||
assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_reader() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let reader = &mut &in_buf[..];
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.len(), 7);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(&buf[..], b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.len(), 3);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(&buf[..], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_reader_vectored() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let reader = &mut &in_buf[..];
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read_vectored(&mut [IoSliceMut::new(&mut buf)]).unwrap(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(
|
||||
reader.read_vectored(&mut [IoSliceMut::new(&mut []), IoSliceMut::new(&mut buf),]).unwrap(),
|
||||
1,
|
||||
);
|
||||
assert_eq!(reader.len(), 7);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf1 = [0; 4];
|
||||
let mut buf2 = [0; 4];
|
||||
assert_eq!(
|
||||
reader
|
||||
.read_vectored(&mut [IoSliceMut::new(&mut buf1), IoSliceMut::new(&mut buf2)],)
|
||||
.unwrap(),
|
||||
7,
|
||||
);
|
||||
let b1: &[_] = &[1, 2, 3, 4];
|
||||
let b2: &[_] = &[5, 6, 7];
|
||||
assert_eq!(buf1, b1);
|
||||
assert_eq!(&buf2[..3], b2);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_exact() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let reader = &mut &in_buf[..];
|
||||
let mut buf = [];
|
||||
assert!(reader.read_exact(&mut buf).is_ok());
|
||||
let mut buf = [8];
|
||||
assert!(reader.read_exact(&mut buf).is_ok());
|
||||
assert_eq!(buf[0], 0);
|
||||
assert_eq!(reader.len(), 7);
|
||||
let mut buf = [0, 0, 0, 0, 0, 0, 0];
|
||||
assert!(reader.read_exact(&mut buf).is_ok());
|
||||
assert_eq!(buf, [1, 2, 3, 4, 5, 6, 7]);
|
||||
assert_eq!(reader.len(), 0);
|
||||
let mut buf = [0];
|
||||
assert!(reader.read_exact(&mut buf).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_reader() {
|
||||
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let mut reader = Cursor::new(&in_buf[..]);
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 1);
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 4);
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 3);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_past_end() {
|
||||
let buf = [0xff];
|
||||
let mut r = Cursor::new(&buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
||||
|
||||
let mut r = Cursor::new(vec![10]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.read(&mut [0]).unwrap(), 0);
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.write(&[3]).unwrap(), 0);
|
||||
|
||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.write(&[3]).unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_past_i64() {
|
||||
let buf = [0xff];
|
||||
let mut r = Cursor::new(&buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
|
||||
let mut r = Cursor::new(vec![10]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[..]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
|
||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||
assert_eq!(r.seek(SeekFrom::Start(6)).unwrap(), 6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x7ffffffffffffff0)).unwrap(), 0x7ffffffffffffff6);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0x10)).unwrap(), 0x8000000000000006);
|
||||
assert_eq!(r.seek(SeekFrom::Current(0)).unwrap(), 0x8000000000000006);
|
||||
assert!(r.seek(SeekFrom::Current(0x7ffffffffffffffd)).is_err());
|
||||
assert_eq!(r.seek(SeekFrom::Current(-0x8000000000000000)).unwrap(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_before_0() {
|
||||
let buf = [0xff];
|
||||
let mut r = Cursor::new(&buf[..]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut r = Cursor::new(vec![10]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[..]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut r = Cursor::new(vec![10].into_boxed_slice());
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seekable_mem_writer() {
|
||||
let mut writer = Cursor::new(Vec::<u8>::new());
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4);
|
||||
assert_eq!(writer.position(), 8);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0);
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[3, 4]).unwrap(), 2);
|
||||
let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3);
|
||||
assert_eq!(writer.write(&[0, 1]).unwrap(), 2);
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7);
|
||||
assert_eq!(writer.write(&[1, 2]).unwrap(), 2);
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10);
|
||||
assert_eq!(writer.write(&[1]).unwrap(), 1);
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
|
||||
assert_eq!(&writer.get_ref()[..], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_seek_past_end() {
|
||||
let mut r = Cursor::new(Vec::new());
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10);
|
||||
assert_eq!(r.write(&[3]).unwrap(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_seek_before_0() {
|
||||
let mut r = Cursor::new(Vec::new());
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
fn vec_seek_and_write_past_usize_max() {
|
||||
let mut c = Cursor::new(Vec::new());
|
||||
c.set_position(usize::MAX as u64 + 1);
|
||||
assert!(c.write_all(&[1, 2, 3]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq() {
|
||||
assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq() {
|
||||
struct AssertEq<T: Eq>(pub T);
|
||||
|
||||
let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn const_cursor() {
|
||||
const CURSOR: Cursor<&[u8]> = Cursor::new(&[0]);
|
||||
const _: &&[u8] = CURSOR.get_ref();
|
||||
const _: u64 = CURSOR.position();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_vec(b: &mut test::Bencher) {
|
||||
let slice = &[1; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut buf = b"some random data to overwrite".to_vec();
|
||||
let mut cursor = Cursor::new(&mut buf);
|
||||
|
||||
let _ = cursor.write_all(slice);
|
||||
test::black_box(&cursor);
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_vec_vectored(b: &mut test::Bencher) {
|
||||
let slices = [
|
||||
IoSlice::new(&[1; 128]),
|
||||
IoSlice::new(&[2; 256]),
|
||||
IoSlice::new(&[3; 512]),
|
||||
IoSlice::new(&[4; 1024]),
|
||||
IoSlice::new(&[5; 2048]),
|
||||
IoSlice::new(&[6; 4096]),
|
||||
IoSlice::new(&[7; 8192]),
|
||||
IoSlice::new(&[8; 8192 * 2]),
|
||||
];
|
||||
|
||||
b.iter(|| {
|
||||
let mut buf = b"some random data to overwrite".to_vec();
|
||||
let mut cursor = Cursor::new(&mut buf);
|
||||
|
||||
let mut slices = slices;
|
||||
let _ = cursor.write_all_vectored(&mut slices);
|
||||
test::black_box(&cursor);
|
||||
})
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error};
|
||||
use crate::sys::io::{decode_error_kind, error_string};
|
||||
use crate::{assert_matches, error, fmt};
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
assert!(size_of::<Error>() <= size_of::<[usize; 2]>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_error() {
|
||||
let code = 6;
|
||||
let msg = error_string(code);
|
||||
let kind = decode_error_kind(code);
|
||||
let err = Error {
|
||||
repr: Repr::new_custom(Box::new(Custom {
|
||||
kind: ErrorKind::InvalidInput,
|
||||
error: Box::new(Error { repr: super::Repr::new_os(code) }),
|
||||
})),
|
||||
};
|
||||
let expected = format!(
|
||||
"Custom {{ \
|
||||
kind: InvalidInput, \
|
||||
error: Os {{ \
|
||||
code: {:?}, \
|
||||
kind: {:?}, \
|
||||
message: {:?} \
|
||||
}} \
|
||||
}}",
|
||||
code, kind, msg
|
||||
);
|
||||
assert_eq!(format!("{err:?}"), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downcasting() {
|
||||
#[derive(Debug)]
|
||||
struct TestError;
|
||||
|
||||
impl fmt::Display for TestError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("asdf")
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for TestError {}
|
||||
|
||||
// we have to call all of these UFCS style right now since method
|
||||
// resolution won't implicitly drop the Send+Sync bounds
|
||||
let mut err = Error::new(ErrorKind::Other, TestError);
|
||||
assert!(err.get_ref().unwrap().is::<TestError>());
|
||||
assert_eq!("asdf", err.get_ref().unwrap().to_string());
|
||||
assert!(err.get_mut().unwrap().is::<TestError>());
|
||||
let extracted = err.into_inner().unwrap();
|
||||
extracted.downcast::<TestError>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_const() {
|
||||
const E: Error = const_error!(ErrorKind::NotFound, "hello");
|
||||
|
||||
assert_eq!(E.kind(), ErrorKind::NotFound);
|
||||
assert_eq!(E.to_string(), "hello");
|
||||
assert!(format!("{E:?}").contains("\"hello\""));
|
||||
assert!(format!("{E:?}").contains("NotFound"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_os_packing() {
|
||||
for code in -20..20 {
|
||||
let e = Error::from_raw_os_error(code);
|
||||
assert_eq!(e.raw_os_error(), Some(code));
|
||||
assert_matches!(
|
||||
e.repr.data(),
|
||||
ErrorData::Os(c) if c == code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_errorkind_packing() {
|
||||
assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound);
|
||||
assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied);
|
||||
assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized);
|
||||
// Check that the innards look like what we want.
|
||||
assert_matches!(
|
||||
Error::from(ErrorKind::OutOfMemory).repr.data(),
|
||||
ErrorData::Simple(ErrorKind::OutOfMemory),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_message_packing() {
|
||||
use super::ErrorKind::*;
|
||||
use super::SimpleMessage;
|
||||
macro_rules! check_simple_msg {
|
||||
($err:expr, $kind:ident, $msg:literal) => {{
|
||||
let e = &$err;
|
||||
// Check that the public api is right.
|
||||
assert_eq!(e.kind(), $kind);
|
||||
assert!(format!("{e:?}").contains($msg));
|
||||
// and we got what we expected
|
||||
assert_matches!(
|
||||
e.repr.data(),
|
||||
ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg })
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
let not_static = const_error!(Uncategorized, "not a constant!");
|
||||
check_simple_msg!(not_static, Uncategorized, "not a constant!");
|
||||
|
||||
const CONST: Error = const_error!(NotFound, "definitely a constant!");
|
||||
check_simple_msg!(CONST, NotFound, "definitely a constant!");
|
||||
|
||||
static STATIC: Error = const_error!(BrokenPipe, "a constant, sort of!");
|
||||
check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!");
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Bojji(bool);
|
||||
impl error::Error for Bojji {}
|
||||
impl fmt::Display for Bojji {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ah! {:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_error_packing() {
|
||||
use super::Custom;
|
||||
let test = Error::new(ErrorKind::Uncategorized, Bojji(true));
|
||||
assert_matches!(
|
||||
test.repr.data(),
|
||||
ErrorData::Custom(Custom {
|
||||
kind: ErrorKind::Uncategorized,
|
||||
error,
|
||||
}) if error.downcast_ref::<Bojji>().as_deref() == Some(&Bojji(true)),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct E;
|
||||
|
||||
impl fmt::Display for E {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for E {}
|
||||
|
||||
#[test]
|
||||
fn test_std_io_error_downcast() {
|
||||
// Case 1: custom error, downcast succeeds
|
||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
||||
let e: Bojji = io_error.downcast().unwrap();
|
||||
assert!(e.0);
|
||||
|
||||
// Case 2: custom error, downcast fails
|
||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
// ensures that the custom error is intact
|
||||
assert_eq!(ErrorKind::Other, io_error.kind());
|
||||
let e: Bojji = io_error.downcast().unwrap();
|
||||
assert!(e.0);
|
||||
|
||||
// Case 3: os error
|
||||
let errno = 20;
|
||||
let io_error = Error::from_raw_os_error(errno);
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(errno, io_error.raw_os_error().unwrap());
|
||||
|
||||
// Case 4: simple
|
||||
let kind = ErrorKind::OutOfMemory;
|
||||
let io_error: Error = kind.into();
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(kind, io_error.kind());
|
||||
|
||||
// Case 5: simple message
|
||||
const SIMPLE_MESSAGE: SimpleMessage =
|
||||
SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" };
|
||||
let io_error = Error::from_static_message(&SIMPLE_MESSAGE);
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind());
|
||||
assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}"));
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
use crate::io::prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn bench_read_slice(b: &mut test::Bencher) {
|
||||
let buf = [5; 1024];
|
||||
let mut dst = [0; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut rd = &buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = rd.read(&mut dst);
|
||||
test::black_box(&dst);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_slice(b: &mut test::Bencher) {
|
||||
let mut buf = [0; 1024];
|
||||
let src = [5; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut wr = &mut buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = wr.write_all(&src);
|
||||
test::black_box(&wr);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_read_vec(b: &mut test::Bencher) {
|
||||
let buf = vec![5; 1024];
|
||||
let mut dst = [0; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut rd = &buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = rd.read(&mut dst);
|
||||
test::black_box(&dst);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_write_vec(b: &mut test::Bencher) {
|
||||
let mut buf = Vec::with_capacity(1024);
|
||||
let src = [5; 128];
|
||||
|
||||
b.iter(|| {
|
||||
let mut wr = &mut buf[..];
|
||||
for _ in 0..8 {
|
||||
let _ = wr.write_all(&src);
|
||||
test::black_box(&wr);
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
use crate::io;
|
||||
use crate::sys::{FromInner, IntoInner, pipe as imp};
|
||||
|
||||
/// Creates an anonymous pipe.
|
||||
///
|
||||
/// # Behavior
|
||||
///
|
||||
/// A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is
|
||||
/// typically used to communicate between two or more separate processes, as there are better,
|
||||
/// faster ways to communicate within a single process.
|
||||
///
|
||||
/// In particular:
|
||||
///
|
||||
/// * A read on a [`PipeReader`] blocks until the pipe is non-empty.
|
||||
/// * A write on a [`PipeWriter`] blocks when the pipe is full.
|
||||
/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
|
||||
/// returns EOF.
|
||||
/// * [`PipeWriter`] can be shared, and multiple processes or threads can write to it at once, but
|
||||
/// writes (above a target-specific threshold) may have their data interleaved.
|
||||
/// * [`PipeReader`] can be shared, and multiple processes or threads can read it at once. Any
|
||||
/// given byte will only get consumed by one reader. There are no guarantees about data
|
||||
/// interleaving.
|
||||
/// * Portable applications cannot assume any atomicity of messages larger than a single byte.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `pipe` function on Unix and the
|
||||
/// `CreatePipe` function on Windows.
|
||||
///
|
||||
/// Note that this [may change in the future][changes].
|
||||
///
|
||||
/// # Capacity
|
||||
///
|
||||
/// Pipe capacity is platform dependent. To quote the Linux [man page]:
|
||||
///
|
||||
/// > Different implementations have different limits for the pipe capacity. Applications should
|
||||
/// > not rely on a particular capacity: an application should be designed so that a reading process
|
||||
/// > consumes data as soon as it is available, so that a writing process does not remain blocked.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #[cfg(miri)] fn main() {}
|
||||
/// # #[cfg(not(miri))]
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// use std::io::{Read, Write, pipe};
|
||||
/// use std::process::Command;
|
||||
/// let (ping_reader, mut ping_writer) = pipe()?;
|
||||
/// let (mut pong_reader, pong_writer) = pipe()?;
|
||||
///
|
||||
/// // Spawn a child process that echoes its input.
|
||||
/// let mut echo_command = Command::new("cat");
|
||||
/// echo_command.stdin(ping_reader);
|
||||
/// echo_command.stdout(pong_writer);
|
||||
/// let mut echo_child = echo_command.spawn()?;
|
||||
///
|
||||
/// // Send input to the child process. Note that because we're writing all the input before we
|
||||
/// // read any output, this could deadlock if the child's input and output pipe buffers both
|
||||
/// // filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer
|
||||
/// // inputs we'd need to read and write at the same time, e.g. using threads.
|
||||
/// ping_writer.write_all(b"hello")?;
|
||||
///
|
||||
/// // `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer
|
||||
/// // remains open. We need to drop our ping writer, or read_to_string will deadlock below.
|
||||
/// drop(ping_writer);
|
||||
///
|
||||
/// // The pong reader can't report EOF while any pong writer remains open. Our Command object is
|
||||
/// // holding a pong writer, and again read_to_string will deadlock if we don't drop it.
|
||||
/// drop(echo_command);
|
||||
///
|
||||
/// let mut buf = String::new();
|
||||
/// // Block until `cat` closes its stdout (a pong writer).
|
||||
/// pong_reader.read_to_string(&mut buf)?;
|
||||
/// assert_eq!(&buf, "hello");
|
||||
///
|
||||
/// // At this point we know `cat` has exited, but we still need to wait to clean up the "zombie".
|
||||
/// echo_child.wait()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
/// [changes]: io#platform-specific-behavior
|
||||
/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
#[inline]
|
||||
pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
|
||||
imp::pipe().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
|
||||
}
|
||||
|
||||
/// Read end of an anonymous pipe.
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
#[derive(Debug)]
|
||||
pub struct PipeReader(pub(crate) imp::Pipe);
|
||||
|
||||
/// Write end of an anonymous pipe.
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
#[derive(Debug)]
|
||||
pub struct PipeWriter(pub(crate) imp::Pipe);
|
||||
|
||||
impl FromInner<imp::Pipe> for PipeReader {
|
||||
fn from_inner(inner: imp::Pipe) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<imp::Pipe> for PipeReader {
|
||||
fn into_inner(self) -> imp::Pipe {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromInner<imp::Pipe> for PipeWriter {
|
||||
fn from_inner(inner: imp::Pipe) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoInner<imp::Pipe> for PipeWriter {
|
||||
fn into_inner(self) -> imp::Pipe {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PipeReader {
|
||||
/// Creates a new [`PipeReader`] instance that shares the same underlying file description.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #[cfg(miri)] fn main() {}
|
||||
/// # #[cfg(not(miri))]
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// use std::fs;
|
||||
/// use std::io::{pipe, Write};
|
||||
/// use std::process::Command;
|
||||
/// const NUM_SLOT: u8 = 2;
|
||||
/// const NUM_PROC: u8 = 5;
|
||||
/// const OUTPUT: &str = "work.txt";
|
||||
///
|
||||
/// let mut jobs = vec![];
|
||||
/// let (reader, mut writer) = pipe()?;
|
||||
///
|
||||
/// // Write NUM_SLOT characters the pipe.
|
||||
/// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
|
||||
///
|
||||
/// // Spawn several processes that read a character from the pipe, do some work, then
|
||||
/// // write back to the pipe. When the pipe is empty, the processes block, so only
|
||||
/// // NUM_SLOT processes can be working at any given time.
|
||||
/// for _ in 0..NUM_PROC {
|
||||
/// jobs.push(
|
||||
/// Command::new("bash")
|
||||
/// .args(["-c",
|
||||
/// &format!(
|
||||
/// "read -n 1\n\
|
||||
/// echo -n 'x' >> '{OUTPUT}'\n\
|
||||
/// echo -n '|'",
|
||||
/// ),
|
||||
/// ])
|
||||
/// .stdin(reader.try_clone()?)
|
||||
/// .stdout(writer.try_clone()?)
|
||||
/// .spawn()?,
|
||||
/// );
|
||||
/// }
|
||||
///
|
||||
/// // Wait for all jobs to finish.
|
||||
/// for mut job in jobs {
|
||||
/// job.wait()?;
|
||||
/// }
|
||||
///
|
||||
/// // Check our work and clean up.
|
||||
/// let xs = fs::read_to_string(OUTPUT)?;
|
||||
/// fs::remove_file(OUTPUT)?;
|
||||
/// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
self.0.try_clone().map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PipeWriter {
|
||||
/// Creates a new [`PipeWriter`] instance that shares the same underlying file description.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// # #[cfg(miri)] fn main() {}
|
||||
/// # #[cfg(not(miri))]
|
||||
/// # fn main() -> std::io::Result<()> {
|
||||
/// use std::process::Command;
|
||||
/// use std::io::{pipe, Read};
|
||||
/// let (mut reader, writer) = pipe()?;
|
||||
///
|
||||
/// // Spawn a process that writes to stdout and stderr.
|
||||
/// let mut peer = Command::new("bash")
|
||||
/// .args([
|
||||
/// "-c",
|
||||
/// "echo -n foo\n\
|
||||
/// echo -n bar >&2"
|
||||
/// ])
|
||||
/// .stdout(writer.try_clone()?)
|
||||
/// .stderr(writer)
|
||||
/// .spawn()?;
|
||||
///
|
||||
/// // Read and check the result.
|
||||
/// let mut msg = String::new();
|
||||
/// reader.read_to_string(&mut msg)?;
|
||||
/// assert_eq!(&msg, "foobar");
|
||||
///
|
||||
/// peer.wait()?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
pub fn try_clone(&self) -> io::Result<Self> {
|
||||
self.0.try_clone().map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Read for &PipeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0.read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Read for PipeReader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0.read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Write for &PipeWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "anonymous_pipe", since = "1.87.0")]
|
||||
impl io::Write for PipeWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use crate::io::{Read, Write, pipe};
|
||||
|
||||
#[test]
|
||||
#[cfg(all(any(unix, windows), not(miri)))]
|
||||
fn pipe_creation_clone_and_rw() {
|
||||
let (rx, tx) = pipe().unwrap();
|
||||
|
||||
tx.try_clone().unwrap().write_all(b"12345").unwrap();
|
||||
drop(tx);
|
||||
|
||||
let mut rx2 = rx.try_clone().unwrap();
|
||||
drop(rx);
|
||||
|
||||
let mut s = String::new();
|
||||
rx2.read_to_string(&mut s).unwrap();
|
||||
drop(rx2);
|
||||
assert_eq!(s, "12345");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,947 +0,0 @@
|
||||
use super::{BorrowedBuf, Cursor, SeekFrom, repeat};
|
||||
use crate::cmp::{self, min};
|
||||
use crate::io::{
|
||||
self, BufRead, BufReader, DEFAULT_BUF_SIZE, IoSlice, IoSliceMut, Read, Seek, Write,
|
||||
};
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ops::Deref;
|
||||
|
||||
#[test]
|
||||
fn read_until() {
|
||||
let mut buf = Cursor::new(&b"12"[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 2);
|
||||
assert_eq!(v, b"12");
|
||||
|
||||
let mut buf = Cursor::new(&b"1233"[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 3);
|
||||
assert_eq!(v, b"123");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 1);
|
||||
assert_eq!(v, b"3");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_until(b'3', &mut v).unwrap(), 0);
|
||||
assert_eq!(v, []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_until() {
|
||||
let bytes: &[u8] = b"read\0ignore\0read\0ignore\0read\0ignore\0";
|
||||
let mut reader = BufReader::new(bytes);
|
||||
|
||||
// read from the bytes, alternating between
|
||||
// consuming `read\0`s and skipping `ignore\0`s
|
||||
loop {
|
||||
// consume `read\0`
|
||||
let mut out = Vec::new();
|
||||
let read = reader.read_until(0, &mut out).unwrap();
|
||||
if read == 0 {
|
||||
// eof
|
||||
break;
|
||||
} else {
|
||||
assert_eq!(out, b"read\0");
|
||||
assert_eq!(read, b"read\0".len());
|
||||
}
|
||||
|
||||
// skip past `ignore\0`
|
||||
let skipped = reader.skip_until(0).unwrap();
|
||||
assert_eq!(skipped, b"ignore\0".len());
|
||||
}
|
||||
|
||||
// ensure we are at the end of the byte slice and that we can skip no further
|
||||
// also ensure skip_until matches the behavior of read_until at EOF
|
||||
let skipped = reader.skip_until(0).unwrap();
|
||||
assert_eq!(skipped, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split() {
|
||||
let buf = Cursor::new(&b"12"[..]);
|
||||
let mut s = buf.split(b'3');
|
||||
assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
|
||||
assert!(s.next().is_none());
|
||||
|
||||
let buf = Cursor::new(&b"1233"[..]);
|
||||
let mut s = buf.split(b'3');
|
||||
assert_eq!(s.next().unwrap().unwrap(), vec![b'1', b'2']);
|
||||
assert_eq!(s.next().unwrap().unwrap(), vec![]);
|
||||
assert!(s.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_line() {
|
||||
let mut buf = Cursor::new(&b"12"[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 2);
|
||||
assert_eq!(v, "12");
|
||||
|
||||
let mut buf = Cursor::new(&b"12\n\n"[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 3);
|
||||
assert_eq!(v, "12\n");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 1);
|
||||
assert_eq!(v, "\n");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_line(&mut v).unwrap(), 0);
|
||||
assert_eq!(v, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines() {
|
||||
let buf = Cursor::new(&b"12\r"[..]);
|
||||
let mut s = buf.lines();
|
||||
assert_eq!(s.next().unwrap().unwrap(), "12\r".to_string());
|
||||
assert!(s.next().is_none());
|
||||
|
||||
let buf = Cursor::new(&b"12\r\n\n"[..]);
|
||||
let mut s = buf.lines();
|
||||
assert_eq!(s.next().unwrap().unwrap(), "12".to_string());
|
||||
assert_eq!(s.next().unwrap().unwrap(), "".to_string());
|
||||
assert!(s.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn buf_read_has_data_left() {
|
||||
let mut buf = Cursor::new(&b"abcd"[..]);
|
||||
assert!(buf.has_data_left().unwrap());
|
||||
buf.read_exact(&mut [0; 2]).unwrap();
|
||||
assert!(buf.has_data_left().unwrap());
|
||||
buf.read_exact(&mut [0; 2]).unwrap();
|
||||
assert!(!buf.has_data_left().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end() {
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(c.read_to_end(&mut v).unwrap(), 0);
|
||||
assert_eq!(v, []);
|
||||
|
||||
let mut c = Cursor::new(&b"1"[..]);
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(c.read_to_end(&mut v).unwrap(), 1);
|
||||
assert_eq!(v, b"1");
|
||||
|
||||
let cap = if cfg!(miri) { 1024 } else { 1024 * 1024 };
|
||||
let data = (0..cap).map(|i| (i / 3) as u8).collect::<Vec<_>>();
|
||||
let mut v = Vec::new();
|
||||
let (a, b) = data.split_at(data.len() / 2);
|
||||
assert_eq!(Cursor::new(a).read_to_end(&mut v).unwrap(), a.len());
|
||||
assert_eq!(Cursor::new(b).read_to_end(&mut v).unwrap(), b.len());
|
||||
assert_eq!(v, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_string() {
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(c.read_to_string(&mut v).unwrap(), 0);
|
||||
assert_eq!(v, "");
|
||||
|
||||
let mut c = Cursor::new(&b"1"[..]);
|
||||
let mut v = String::new();
|
||||
assert_eq!(c.read_to_string(&mut v).unwrap(), 1);
|
||||
assert_eq!(v, "1");
|
||||
|
||||
let mut c = Cursor::new(&b"\xff"[..]);
|
||||
let mut v = String::new();
|
||||
assert!(c.read_to_string(&mut v).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_exact() {
|
||||
let mut buf = [0; 4];
|
||||
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
|
||||
let mut c = Cursor::new(&b"123"[..]).chain(Cursor::new(&b"456789"[..]));
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"1234");
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"5678");
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_exact_slice() {
|
||||
let mut buf = [0; 4];
|
||||
|
||||
let mut c = &b""[..];
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
|
||||
let mut c = &b"123"[..];
|
||||
assert_eq!(c.read_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
// make sure the optimized (early returning) method is being used
|
||||
assert_eq!(&buf, &[0; 4]);
|
||||
|
||||
let mut c = &b"1234"[..];
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"1234");
|
||||
|
||||
let mut c = &b"56789"[..];
|
||||
c.read_exact(&mut buf).unwrap();
|
||||
assert_eq!(&buf, b"5678");
|
||||
assert_eq!(c, b"9");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_exact() {
|
||||
let buf: &mut [_] = &mut [0; 4];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
|
||||
let mut c = Cursor::new(&b""[..]);
|
||||
assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
|
||||
let mut c = Cursor::new(&b"123456789"[..]);
|
||||
c.read_buf_exact(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.filled(), b"1234");
|
||||
|
||||
buf.clear();
|
||||
|
||||
c.read_buf_exact(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.filled(), b"5678");
|
||||
|
||||
buf.clear();
|
||||
|
||||
assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn borrowed_cursor_advance_overflow() {
|
||||
let mut buf = [0; 512];
|
||||
let mut buf = BorrowedBuf::from(&mut buf[..]);
|
||||
buf.unfilled().advance(1);
|
||||
buf.unfilled().advance(usize::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_eof() {
|
||||
struct R;
|
||||
|
||||
impl Read for R {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::const_error!(io::ErrorKind::Other, ""))
|
||||
}
|
||||
}
|
||||
impl BufRead for R {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
Err(io::const_error!(io::ErrorKind::Other, ""))
|
||||
}
|
||||
fn consume(&mut self, _amt: usize) {}
|
||||
}
|
||||
|
||||
let mut buf = [0; 1];
|
||||
assert_eq!(0, R.take(0).read(&mut buf).unwrap());
|
||||
assert_eq!(b"", R.take(0).fill_buf().unwrap());
|
||||
}
|
||||
|
||||
fn cmp_bufread<Br1: BufRead, Br2: BufRead>(mut br1: Br1, mut br2: Br2, exp: &[u8]) {
|
||||
let mut cat = Vec::new();
|
||||
loop {
|
||||
let consume = {
|
||||
let buf1 = br1.fill_buf().unwrap();
|
||||
let buf2 = br2.fill_buf().unwrap();
|
||||
let minlen = if buf1.len() < buf2.len() { buf1.len() } else { buf2.len() };
|
||||
assert_eq!(buf1[..minlen], buf2[..minlen]);
|
||||
cat.extend_from_slice(&buf1[..minlen]);
|
||||
minlen
|
||||
};
|
||||
if consume == 0 {
|
||||
break;
|
||||
}
|
||||
br1.consume(consume);
|
||||
br2.consume(consume);
|
||||
}
|
||||
assert_eq!(br1.fill_buf().unwrap().len(), 0);
|
||||
assert_eq!(br2.fill_buf().unwrap().len(), 0);
|
||||
assert_eq!(&cat[..], &exp[..])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_bufread() {
|
||||
let testdata = b"ABCDEFGHIJKL";
|
||||
let chain1 =
|
||||
(&testdata[..3]).chain(&testdata[3..6]).chain(&testdata[6..9]).chain(&testdata[9..]);
|
||||
let chain2 = (&testdata[..4]).chain(&testdata[4..8]).chain(&testdata[8..]);
|
||||
cmp_bufread(chain1, chain2, &testdata[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_splitted_char() {
|
||||
let chain = b"\xc3".chain(b"\xa9".as_slice());
|
||||
assert_eq!(crate::io::read_to_string(chain).unwrap(), "é");
|
||||
|
||||
let mut chain = b"\xc3".chain(b"\xa9\n".as_slice());
|
||||
let mut buf = String::new();
|
||||
assert_eq!(chain.read_line(&mut buf).unwrap(), 3);
|
||||
assert_eq!(buf, "é\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bufreader_size_hint() {
|
||||
let testdata = b"ABCDEFGHIJKL";
|
||||
let mut buf_reader = BufReader::new(&testdata[..]);
|
||||
assert_eq!(buf_reader.buffer().len(), 0);
|
||||
|
||||
let buffer_length = testdata.len();
|
||||
buf_reader.fill_buf().unwrap();
|
||||
|
||||
// Check that size hint matches buffer contents
|
||||
let mut buffered_bytes = buf_reader.bytes();
|
||||
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
||||
assert_eq!(lower_bound, buffer_length);
|
||||
|
||||
// Check that size hint matches buffer contents after advancing
|
||||
buffered_bytes.next().unwrap().unwrap();
|
||||
let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
|
||||
assert_eq!(lower_bound, buffer_length - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_size_hint() {
|
||||
let size_hint = io::empty().bytes().size_hint();
|
||||
assert_eq!(size_hint, (0, Some(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_size_hint() {
|
||||
let size_hint = (&[1, 2, 3]).bytes().size_hint();
|
||||
assert_eq!(size_hint, (3, Some(3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_size_hint() {
|
||||
let size_hint = (&[1, 2, 3]).take(2).bytes().size_hint();
|
||||
assert_eq!(size_hint, (2, Some(2)));
|
||||
|
||||
let size_hint = (&[1, 2, 3]).take(4).bytes().size_hint();
|
||||
assert_eq!(size_hint, (3, Some(3)));
|
||||
|
||||
let size_hint = io::repeat(0).take(3).bytes().size_hint();
|
||||
assert_eq!(size_hint, (3, Some(3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_empty_size_hint() {
|
||||
let chain = io::empty().chain(io::empty());
|
||||
let size_hint = chain.bytes().size_hint();
|
||||
assert_eq!(size_hint, (0, Some(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_size_hint() {
|
||||
let testdata = b"ABCDEFGHIJKL";
|
||||
let mut buf_reader_1 = BufReader::new(&testdata[..6]);
|
||||
let mut buf_reader_2 = BufReader::new(&testdata[6..]);
|
||||
|
||||
buf_reader_1.fill_buf().unwrap();
|
||||
buf_reader_2.fill_buf().unwrap();
|
||||
|
||||
let chain = buf_reader_1.chain(buf_reader_2);
|
||||
let size_hint = chain.bytes().size_hint();
|
||||
assert_eq!(size_hint, (testdata.len(), Some(testdata.len())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_zero_length_read_is_not_eof() {
|
||||
let a = b"A";
|
||||
let b = b"B";
|
||||
let mut s = String::new();
|
||||
let mut chain = (&a[..]).chain(&b[..]);
|
||||
chain.read(&mut []).unwrap();
|
||||
chain.read_to_string(&mut s).unwrap();
|
||||
assert_eq!("AB", s);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[cfg_attr(miri, ignore)] // Miri isn't fast...
|
||||
fn bench_read_to_end(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let mut lr = repeat(1).take(10000000);
|
||||
let mut vec = Vec::with_capacity(1024);
|
||||
super::default_read_to_end(&mut lr, &mut vec, None)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_len() -> io::Result<()> {
|
||||
let mut c = Cursor::new(vec![0; 15]);
|
||||
assert_eq!(c.stream_len()?, 15);
|
||||
|
||||
c.seek(SeekFrom::End(0))?;
|
||||
let old_pos = c.stream_position()?;
|
||||
assert_eq!(c.stream_len()?, 15);
|
||||
assert_eq!(c.stream_position()?, old_pos);
|
||||
|
||||
c.seek(SeekFrom::Start(7))?;
|
||||
c.seek(SeekFrom::Current(2))?;
|
||||
let old_pos = c.stream_position()?;
|
||||
assert_eq!(c.stream_len()?, 15);
|
||||
assert_eq!(c.stream_position()?, old_pos);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_position() -> io::Result<()> {
|
||||
// All `asserts` are duplicated here to make sure the method does not
|
||||
// change anything about the seek state.
|
||||
let mut c = Cursor::new(vec![0; 15]);
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
|
||||
c.seek(SeekFrom::End(0))?;
|
||||
assert_eq!(c.stream_position()?, 15);
|
||||
assert_eq!(c.stream_position()?, 15);
|
||||
|
||||
c.seek(SeekFrom::Start(7))?;
|
||||
c.seek(SeekFrom::Current(2))?;
|
||||
assert_eq!(c.stream_position()?, 9);
|
||||
assert_eq!(c.stream_position()?, 9);
|
||||
|
||||
c.seek(SeekFrom::End(-3))?;
|
||||
c.seek(SeekFrom::Current(1))?;
|
||||
c.seek(SeekFrom::Current(-5))?;
|
||||
assert_eq!(c.stream_position()?, 8);
|
||||
assert_eq!(c.stream_position()?, 8);
|
||||
|
||||
c.rewind()?;
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
assert_eq!(c.stream_position()?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_seek() -> io::Result<()> {
|
||||
let mut buf = Cursor::new(b"0123456789");
|
||||
buf.set_position(2);
|
||||
let mut take = buf.by_ref().take(4);
|
||||
let mut buf1 = [0u8; 1];
|
||||
let mut buf2 = [0u8; 2];
|
||||
assert_eq!(take.position(), 0);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Start(0))?, 0);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'2', b'3']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(1))?, 1);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'3', b'4']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(2))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(3))?, 3);
|
||||
take.read_exact(&mut buf1)?;
|
||||
assert_eq!(buf1, [b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::Start(4))?, 4);
|
||||
assert_eq!(take.read(&mut buf1)?, 0);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::End(0))?, 4);
|
||||
assert_eq!(take.seek(SeekFrom::End(-1))?, 3);
|
||||
take.read_exact(&mut buf1)?;
|
||||
assert_eq!(buf1, [b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::End(-2))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
assert_eq!(take.seek(SeekFrom::End(-3))?, 1);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'3', b'4']);
|
||||
assert_eq!(take.seek(SeekFrom::End(-4))?, 0);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'2', b'3']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(0))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(-3))?, 1);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'3', b'4']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(-1))?, 2);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'4', b'5']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(-4))?, 0);
|
||||
take.read_exact(&mut buf2)?;
|
||||
assert_eq!(buf2, [b'2', b'3']);
|
||||
|
||||
assert_eq!(take.seek(SeekFrom::Current(2))?, 4);
|
||||
assert_eq!(take.read(&mut buf1)?, 0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_seek_error() {
|
||||
let buf = Cursor::new(b"0123456789");
|
||||
let mut take = buf.take(2);
|
||||
assert!(take.seek(SeekFrom::Start(3)).is_err());
|
||||
assert!(take.seek(SeekFrom::End(1)).is_err());
|
||||
assert!(take.seek(SeekFrom::End(-3)).is_err());
|
||||
assert!(take.seek(SeekFrom::Current(-1)).is_err());
|
||||
assert!(take.seek(SeekFrom::Current(3)).is_err());
|
||||
}
|
||||
|
||||
struct ExampleHugeRangeOfZeroes {
|
||||
position: u64,
|
||||
}
|
||||
|
||||
impl Read for ExampleHugeRangeOfZeroes {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let max = buf.len().min(usize::MAX);
|
||||
for i in 0..max {
|
||||
if self.position == u64::MAX {
|
||||
return Ok(i);
|
||||
}
|
||||
self.position += 1;
|
||||
buf[i] = 0;
|
||||
}
|
||||
Ok(max)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for ExampleHugeRangeOfZeroes {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
match pos {
|
||||
io::SeekFrom::Start(i) => self.position = i,
|
||||
io::SeekFrom::End(i) if i >= 0 => self.position = u64::MAX,
|
||||
io::SeekFrom::End(i) => self.position = self.position - i.unsigned_abs(),
|
||||
io::SeekFrom::Current(i) => {
|
||||
self.position = if i >= 0 {
|
||||
self.position.saturating_add(i.unsigned_abs())
|
||||
} else {
|
||||
self.position.saturating_sub(i.unsigned_abs())
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(self.position)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_seek_big_offsets() -> io::Result<()> {
|
||||
let inner = ExampleHugeRangeOfZeroes { position: 1 };
|
||||
let mut take = inner.take(u64::MAX - 2);
|
||||
assert_eq!(take.seek(io::SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2);
|
||||
assert_eq!(take.inner.position, u64::MAX - 1);
|
||||
assert_eq!(take.seek(io::SeekFrom::Start(0))?, 0);
|
||||
assert_eq!(take.inner.position, 1);
|
||||
assert_eq!(take.seek(io::SeekFrom::End(-1))?, u64::MAX - 3);
|
||||
assert_eq!(take.inner.position, u64::MAX - 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// A simple example reader which uses the default implementation of
|
||||
// read_to_end.
|
||||
struct ExampleSliceReader<'a> {
|
||||
slice: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Read for ExampleSliceReader<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let len = cmp::min(self.slice.len(), buf.len());
|
||||
buf[..len].copy_from_slice(&self.slice[..len]);
|
||||
self.slice = &self.slice[len..];
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_to_end_capacity() -> io::Result<()> {
|
||||
let input = &b"foo"[..];
|
||||
|
||||
// read_to_end() takes care not to over-allocate when a buffer is the
|
||||
// exact size needed.
|
||||
let mut vec1 = Vec::with_capacity(input.len());
|
||||
ExampleSliceReader { slice: input }.read_to_end(&mut vec1)?;
|
||||
assert_eq!(vec1.len(), input.len());
|
||||
assert_eq!(vec1.capacity(), input.len(), "did not allocate more");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_mut_advance_slices() {
|
||||
let mut buf1 = [1; 8];
|
||||
let mut buf2 = [2; 16];
|
||||
let mut buf3 = [3; 8];
|
||||
let mut bufs = &mut [
|
||||
IoSliceMut::new(&mut buf1),
|
||||
IoSliceMut::new(&mut buf2),
|
||||
IoSliceMut::new(&mut buf3),
|
||||
][..];
|
||||
|
||||
// Only in a single buffer..
|
||||
IoSliceMut::advance_slices(&mut bufs, 1);
|
||||
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer, leaving others as is.
|
||||
IoSliceMut::advance_slices(&mut bufs, 7);
|
||||
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer and removing from the next buffer.
|
||||
IoSliceMut::advance_slices(&mut bufs, 18);
|
||||
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_mut_advance_slices_empty_slice() {
|
||||
let mut empty_bufs = &mut [][..];
|
||||
IoSliceMut::advance_slices(&mut empty_bufs, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_mut_advance_slices_beyond_total_length() {
|
||||
let mut buf1 = [1; 8];
|
||||
let mut bufs = &mut [IoSliceMut::new(&mut buf1)][..];
|
||||
|
||||
IoSliceMut::advance_slices(&mut bufs, 9);
|
||||
assert!(bufs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_advance_slices() {
|
||||
let buf1 = [1; 8];
|
||||
let buf2 = [2; 16];
|
||||
let buf3 = [3; 8];
|
||||
let mut bufs = &mut [IoSlice::new(&buf1), IoSlice::new(&buf2), IoSlice::new(&buf3)][..];
|
||||
|
||||
// Only in a single buffer..
|
||||
IoSlice::advance_slices(&mut bufs, 1);
|
||||
assert_eq!(bufs[0].deref(), [1; 7].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[2].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer, leaving others as is.
|
||||
IoSlice::advance_slices(&mut bufs, 7);
|
||||
assert_eq!(bufs[0].deref(), [2; 16].as_ref());
|
||||
assert_eq!(bufs[1].deref(), [3; 8].as_ref());
|
||||
|
||||
// Removing a buffer and removing from the next buffer.
|
||||
IoSlice::advance_slices(&mut bufs, 18);
|
||||
assert_eq!(bufs[0].deref(), [3; 6].as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_advance_slices_empty_slice() {
|
||||
let mut empty_bufs = &mut [][..];
|
||||
IoSlice::advance_slices(&mut empty_bufs, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn io_slice_advance_slices_beyond_total_length() {
|
||||
let buf1 = [1; 8];
|
||||
let mut bufs = &mut [IoSlice::new(&buf1)][..];
|
||||
|
||||
IoSlice::advance_slices(&mut bufs, 9);
|
||||
assert!(bufs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_as_slice() {
|
||||
let buf = [1; 8];
|
||||
let slice = IoSlice::new(&buf).as_slice();
|
||||
assert_eq!(slice, buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn io_slice_into_slice() {
|
||||
let mut buf = [1; 8];
|
||||
let slice = IoSliceMut::new(&mut buf).into_slice();
|
||||
assert_eq!(slice, [1; 8]);
|
||||
}
|
||||
|
||||
/// Creates a new writer that reads from at most `n_bufs` and reads
|
||||
/// `per_call` bytes (in total) per call to write.
|
||||
fn test_writer(n_bufs: usize, per_call: usize) -> TestWriter {
|
||||
TestWriter { n_bufs, per_call, written: Vec::new() }
|
||||
}
|
||||
|
||||
struct TestWriter {
|
||||
n_bufs: usize,
|
||||
per_call: usize,
|
||||
written: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Write for TestWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.write_vectored(&[IoSlice::new(buf)])
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||
let mut left = self.per_call;
|
||||
let mut written = 0;
|
||||
for buf in bufs.iter().take(self.n_bufs) {
|
||||
let n = min(left, buf.len());
|
||||
self.written.extend_from_slice(&buf[0..n]);
|
||||
left -= n;
|
||||
written += n;
|
||||
}
|
||||
Ok(written)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_writer_read_from_one_buf() {
|
||||
let mut writer = test_writer(1, 2);
|
||||
|
||||
assert_eq!(writer.write(&[]).unwrap(), 0);
|
||||
assert_eq!(writer.write_vectored(&[]).unwrap(), 0);
|
||||
|
||||
// Read at most 2 bytes.
|
||||
assert_eq!(writer.write(&[1, 1, 1]).unwrap(), 2);
|
||||
let bufs = &[IoSlice::new(&[2, 2, 2])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 2);
|
||||
|
||||
// Only read from first buf.
|
||||
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4, 4])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 1);
|
||||
|
||||
assert_eq!(writer.written, &[1, 1, 2, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_writer_read_from_multiple_bufs() {
|
||||
let mut writer = test_writer(3, 3);
|
||||
|
||||
// Read at most 3 bytes from two buffers.
|
||||
let bufs = &[IoSlice::new(&[1]), IoSlice::new(&[2, 2, 2])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
||||
|
||||
// Read at most 3 bytes from three buffers.
|
||||
let bufs = &[IoSlice::new(&[3]), IoSlice::new(&[4]), IoSlice::new(&[5, 5])];
|
||||
assert_eq!(writer.write_vectored(bufs).unwrap(), 3);
|
||||
|
||||
assert_eq!(writer.written, &[1, 2, 2, 3, 4, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_all_vectored() {
|
||||
#[rustfmt::skip] // Becomes unreadable otherwise.
|
||||
let tests: Vec<(_, &'static [u8])> = vec![
|
||||
(vec![], &[]),
|
||||
(vec![IoSlice::new(&[]), IoSlice::new(&[])], &[]),
|
||||
(vec![IoSlice::new(&[1])], &[1]),
|
||||
(vec![IoSlice::new(&[1, 2])], &[1, 2]),
|
||||
(vec![IoSlice::new(&[1, 2, 3])], &[1, 2, 3]),
|
||||
(vec![IoSlice::new(&[1, 2, 3, 4])], &[1, 2, 3, 4]),
|
||||
(vec![IoSlice::new(&[1, 2, 3, 4, 5])], &[1, 2, 3, 4, 5]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2])], &[1, 2]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2])], &[1, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2])], &[1, 1, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2])], &[1, 1, 1, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 2, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1, 1, 1, 1]), IoSlice::new(&[2, 2, 2, 2])], &[1, 1, 1, 1, 2, 2, 2, 2]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2]), IoSlice::new(&[3])], &[1, 2, 3]),
|
||||
(vec![IoSlice::new(&[1, 1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3])], &[1, 1, 2, 2, 3, 3]),
|
||||
(vec![IoSlice::new(&[1]), IoSlice::new(&[2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 2, 2, 3, 3, 3]),
|
||||
(vec![IoSlice::new(&[1, 1, 1]), IoSlice::new(&[2, 2, 2]), IoSlice::new(&[3, 3, 3])], &[1, 1, 1, 2, 2, 2, 3, 3, 3]),
|
||||
];
|
||||
|
||||
let writer_configs = &[(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)];
|
||||
|
||||
for (n_bufs, per_call) in writer_configs.iter().copied() {
|
||||
for (mut input, wanted) in tests.clone().into_iter() {
|
||||
let mut writer = test_writer(n_bufs, per_call);
|
||||
assert!(writer.write_all_vectored(&mut *input).is_ok());
|
||||
assert_eq!(&*writer.written, &*wanted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 94981
|
||||
#[test]
|
||||
#[should_panic = "number of read bytes exceeds limit"]
|
||||
fn test_take_wrong_length() {
|
||||
struct LieAboutSize(bool);
|
||||
|
||||
impl Read for LieAboutSize {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// Lie about the read size at first time of read.
|
||||
if core::mem::take(&mut self.0) { Ok(buf.len() + 1) } else { Ok(buf.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
let mut buffer = vec![0; 4];
|
||||
let mut reader = LieAboutSize(true).take(4);
|
||||
// Primed the `Limit` by lying about the read size.
|
||||
let _ = reader.read(&mut buffer[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slice_read_exact_eof() {
|
||||
let slice = &b"123456"[..];
|
||||
|
||||
let mut r = slice;
|
||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
||||
assert!(r.is_empty());
|
||||
|
||||
let mut r = slice;
|
||||
let buf = &mut [0; 10];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
||||
assert!(r.is_empty());
|
||||
assert_eq!(buf.filled(), b"123456");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cursor_read_exact_eof() {
|
||||
let slice = Cursor::new(b"123456");
|
||||
|
||||
let mut r = slice.clone();
|
||||
assert!(r.read_exact(&mut [0; 10]).is_err());
|
||||
assert!(Cursor::split(&r).1.is_empty());
|
||||
|
||||
let mut r = slice;
|
||||
let buf = &mut [0; 10];
|
||||
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
|
||||
assert!(r.read_buf_exact(buf.unfilled()).is_err());
|
||||
assert!(Cursor::split(&r).1.is_empty());
|
||||
assert_eq!(buf.filled(), b"123456");
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_take_read(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let mut buf = [0; 64];
|
||||
|
||||
[255; 128].take(64).read(&mut buf).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_take_read_buf(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 64];
|
||||
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
|
||||
[255; 128].take(64).read_buf(buf.unfilled()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// Issue #120603
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn read_buf_broken_read() {
|
||||
struct MalformedRead;
|
||||
|
||||
impl Read for MalformedRead {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// broken length calculation
|
||||
Ok(buf.len() + 1)
|
||||
}
|
||||
}
|
||||
|
||||
let _ = BufReader::new(MalformedRead).fill_buf();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_full_read() {
|
||||
struct FullRead;
|
||||
|
||||
impl Read for FullRead {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(BufReader::new(FullRead).fill_buf().unwrap().len(), DEFAULT_BUF_SIZE);
|
||||
}
|
||||
|
||||
struct DataAndErrorReader(&'static [u8]);
|
||||
|
||||
impl Read for DataAndErrorReader {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
panic!("We want tests to use `read_buf`")
|
||||
}
|
||||
|
||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
||||
self.0.read_buf(buf).unwrap();
|
||||
Err(io::Error::other("error"))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_data_and_error_take() {
|
||||
let mut buf = [0; 64];
|
||||
let mut buf = io::BorrowedBuf::from(buf.as_mut_slice());
|
||||
|
||||
let mut r = DataAndErrorReader(&[4, 5, 6]).take(1);
|
||||
assert!(r.read_buf(buf.unfilled()).is_err());
|
||||
assert_eq!(buf.filled(), &[4]);
|
||||
|
||||
assert!(r.read_buf(buf.unfilled()).is_ok());
|
||||
assert_eq!(buf.filled(), &[4]);
|
||||
assert_eq!(r.get_ref().0, &[5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_data_and_error_buf() {
|
||||
let mut r = BufReader::new(DataAndErrorReader(&[4, 5, 6]));
|
||||
|
||||
assert!(r.fill_buf().is_err());
|
||||
assert_eq!(r.fill_buf().unwrap(), &[4, 5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_buf_data_and_error_read_to_end() {
|
||||
let mut r = DataAndErrorReader(&[4, 5, 6]);
|
||||
|
||||
let mut v = Vec::with_capacity(200);
|
||||
assert!(r.read_to_end(&mut v).is_err());
|
||||
|
||||
assert_eq!(v, &[4, 5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end_error() {
|
||||
struct ErrorReader;
|
||||
|
||||
impl Read for ErrorReader {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
|
||||
Err(io::Error::other("error"))
|
||||
}
|
||||
}
|
||||
|
||||
let mut r = [4, 5, 6].chain(ErrorReader);
|
||||
|
||||
let mut v = Vec::with_capacity(200);
|
||||
assert!(r.read_to_end(&mut v).is_err());
|
||||
|
||||
assert_eq!(v, &[4, 5, 6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_oom_error() {
|
||||
use alloc::alloc::Layout;
|
||||
use alloc::collections::{TryReserveError, TryReserveErrorKind};
|
||||
|
||||
// We simulate a `Vec::try_reserve` error rather than attempting a huge size for real. This way
|
||||
// we're not subject to the whims of optimization that might skip the actual allocation, and it
|
||||
// also works for 32-bit targets and miri that might not OOM at all.
|
||||
let layout = Layout::new::<u8>();
|
||||
let kind = TryReserveErrorKind::AllocError { layout, non_exhaustive: () };
|
||||
let reserve_err = TryReserveError::from(kind);
|
||||
|
||||
let io_err = io::Error::from(reserve_err);
|
||||
assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
use crate::fmt;
|
||||
use crate::io::prelude::*;
|
||||
use crate::io::{
|
||||
BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink,
|
||||
};
|
||||
use crate::mem::MaybeUninit;
|
||||
|
||||
struct ErrorDisplay;
|
||||
|
||||
impl fmt::Display for ErrorDisplay {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
}
|
||||
|
||||
struct PanicDisplay;
|
||||
|
||||
impl fmt::Display for PanicDisplay {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn test_sinking<W: Write>(mut w: W) {
|
||||
assert_eq!(w.write(&[]).unwrap(), 0);
|
||||
assert_eq!(w.write(&[0]).unwrap(), 1);
|
||||
assert_eq!(w.write(&[0; 1024]).unwrap(), 1024);
|
||||
w.write_all(&[]).unwrap();
|
||||
w.write_all(&[0]).unwrap();
|
||||
w.write_all(&[0; 1024]).unwrap();
|
||||
let mut bufs =
|
||||
[IoSlice::new(&[]), IoSlice::new(&[0]), IoSlice::new(&[0; 1024]), IoSlice::new(&[])];
|
||||
assert!(w.is_write_vectored());
|
||||
assert_eq!(w.write_vectored(&[]).unwrap(), 0);
|
||||
assert_eq!(w.write_vectored(&bufs).unwrap(), 1025);
|
||||
w.write_all_vectored(&mut []).unwrap();
|
||||
w.write_all_vectored(&mut bufs).unwrap();
|
||||
assert!(w.flush().is_ok());
|
||||
assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024);
|
||||
// Ignores fmt arguments
|
||||
w.write_fmt(format_args!("{}", ErrorDisplay)).unwrap();
|
||||
w.write_fmt(format_args!("{}", PanicDisplay)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sink_sinks() {
|
||||
test_sinking(sink());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_reads() {
|
||||
let mut e = empty();
|
||||
assert_eq!(e.read(&mut []).unwrap(), 0);
|
||||
assert_eq!(e.read(&mut [0]).unwrap(), 0);
|
||||
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
|
||||
assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0);
|
||||
|
||||
e.read_exact(&mut []).unwrap();
|
||||
assert_eq!(e.read_exact(&mut [0]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
assert_eq!(e.read_exact(&mut [0; 1024]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
|
||||
assert!(!e.is_read_vectored());
|
||||
assert_eq!(e.read_vectored(&mut []).unwrap(), 0);
|
||||
let (mut buf1, mut buf1024) = ([0], [0; 1024]);
|
||||
let bufs = &mut [
|
||||
IoSliceMut::new(&mut []),
|
||||
IoSliceMut::new(&mut buf1),
|
||||
IoSliceMut::new(&mut buf1024),
|
||||
IoSliceMut::new(&mut []),
|
||||
];
|
||||
assert_eq!(e.read_vectored(bufs).unwrap(), 0);
|
||||
|
||||
let buf: &mut [MaybeUninit<_>] = &mut [];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [MaybeUninit<_>] = &mut [];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
e.read_buf_exact(buf.unfilled()).unwrap();
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit()];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
|
||||
let mut buf: BorrowedBuf<'_> = buf.into();
|
||||
assert_eq!(
|
||||
Read::by_ref(&mut e).read_buf_exact(buf.unfilled()).unwrap_err().kind(),
|
||||
ErrorKind::UnexpectedEof,
|
||||
);
|
||||
assert_eq!(buf.len(), 0);
|
||||
assert_eq!(buf.init_len(), 0);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, vec![]);
|
||||
let mut buf = vec![1, 2, 3];
|
||||
assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, vec![1, 2, 3]);
|
||||
|
||||
let mut buf = String::new();
|
||||
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, "");
|
||||
let mut buf = "hello".to_owned();
|
||||
assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
|
||||
assert_eq!(buf, "hello");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_seeks() {
|
||||
let mut e = empty();
|
||||
assert!(matches!(e.seek(SeekFrom::Start(0)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Start(1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Start(u64::MAX)), Ok(0)));
|
||||
|
||||
assert!(matches!(e.seek(SeekFrom::End(i64::MIN)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(-1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(0)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::End(i64::MAX)), Ok(0)));
|
||||
|
||||
assert!(matches!(e.seek(SeekFrom::Current(i64::MIN)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(-1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(0)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(1)), Ok(0)));
|
||||
assert!(matches!(e.seek(SeekFrom::Current(i64::MAX)), Ok(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_sinks() {
|
||||
test_sinking(empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeat_repeats() {
|
||||
let mut r = repeat(4);
|
||||
let mut b = [0; 1024];
|
||||
assert_eq!(r.read(&mut b).unwrap(), 1024);
|
||||
assert!(b.iter().all(|b| *b == 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_some_bytes() {
|
||||
assert_eq!(repeat(4).take(100).bytes().count(), 100);
|
||||
assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4);
|
||||
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn const_utils() {
|
||||
const _: Empty = empty();
|
||||
const _: Repeat = repeat(b'c');
|
||||
const _: Sink = sink();
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
#![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,
|
||||
unfulfilled_lint_expectations
|
||||
)]
|
||||
#![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 bstr;
|
||||
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!();
|
||||
};
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
//! 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};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,181 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
macro_rules! rtabort {
|
||||
($($t:tt)*) => {{}};
|
||||
}
|
||||
macro_rules! rtprintpanic {
|
||||
($($t:tt)*) => {{}};
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
pub mod barrier;
|
||||
pub mod lazy_lock;
|
||||
pub mod mpmc;
|
||||
pub mod mpsc;
|
||||
pub mod nonpoison;
|
||||
pub mod once;
|
||||
pub mod once_lock;
|
||||
pub mod poison;
|
||||
pub mod reentrant_lock;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use core::sync::atomic;
|
||||
|
||||
pub use once::Once;
|
||||
pub use once::OnceState;
|
||||
|
||||
pub use poison::LockResult;
|
||||
pub use poison::Mutex;
|
||||
pub use poison::PoisonError;
|
||||
pub use poison::TryLockError;
|
||||
pub use poison::TryLockResult;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
pub struct WaitTimeoutResult(bool);
|
||||
@@ -1,167 +0,0 @@
|
||||
use crate::fmt;
|
||||
use core::panic::RefUnwindSafe;
|
||||
use crate::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
/// A barrier enables multiple threads to synchronize the beginning
|
||||
/// of some computation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let n = 10;
|
||||
/// let barrier = Barrier::new(n);
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..n {
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// s.spawn(|| {
|
||||
/// println!("before wait");
|
||||
/// barrier.wait();
|
||||
/// println!("after wait");
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Barrier {
|
||||
lock: Mutex<BarrierState>,
|
||||
cvar: Condvar,
|
||||
num_threads: usize,
|
||||
}
|
||||
|
||||
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
|
||||
impl RefUnwindSafe for Barrier {}
|
||||
|
||||
// The inner state of a double barrier
|
||||
struct BarrierState {
|
||||
count: usize,
|
||||
generation_id: usize,
|
||||
}
|
||||
|
||||
/// A `BarrierWaitResult` is returned by [`Barrier::wait()`] when all threads
|
||||
/// in the [`Barrier`] have rendezvoused.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(1);
|
||||
/// let barrier_wait_result = barrier.wait();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct BarrierWaitResult(bool);
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Barrier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Barrier").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl Barrier {
|
||||
/// Creates a new barrier that can block a given number of threads.
|
||||
///
|
||||
/// A barrier will block all threads which call [`wait()`] until the `n`th thread calls [`wait()`],
|
||||
/// and then wake up all threads at once.
|
||||
///
|
||||
/// [`wait()`]: Barrier::wait
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(10);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_barrier", since = "1.78.0")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn new(n: usize) -> Barrier {
|
||||
Barrier {
|
||||
lock: Mutex::new(BarrierState { count: 0, generation_id: 0 }),
|
||||
cvar: Condvar::new(),
|
||||
num_threads: n,
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until all threads have rendezvoused here.
|
||||
///
|
||||
/// Barriers are re-usable after all threads have rendezvoused once, and can
|
||||
/// be used continuously.
|
||||
///
|
||||
/// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
|
||||
/// returns `true` from [`BarrierWaitResult::is_leader()`] when returning
|
||||
/// from this function, and all other threads will receive a result that
|
||||
/// will return `false` from [`BarrierWaitResult::is_leader()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let n = 10;
|
||||
/// let barrier = Barrier::new(n);
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..n {
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// s.spawn(|| {
|
||||
/// println!("before wait");
|
||||
/// barrier.wait();
|
||||
/// println!("after wait");
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn wait(&self) -> BarrierWaitResult {
|
||||
let mut lock = self.lock.lock();
|
||||
let local_gen = lock.generation_id;
|
||||
lock.count += 1;
|
||||
if lock.count < self.num_threads {
|
||||
self.cvar.wait_while(&mut lock, |state| local_gen == state.generation_id);
|
||||
BarrierWaitResult(false)
|
||||
} else {
|
||||
lock.count = 0;
|
||||
lock.generation_id = lock.generation_id.wrapping_add(1);
|
||||
self.cvar.notify_all();
|
||||
BarrierWaitResult(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for BarrierWaitResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BarrierWaitResult").field("is_leader", &self.is_leader()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl BarrierWaitResult {
|
||||
/// Returns `true` if this thread is the "leader thread" for the call to
|
||||
/// [`Barrier::wait()`].
|
||||
///
|
||||
/// Only one thread will have `true` returned from their result, all other
|
||||
/// threads will have `false` returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Barrier;
|
||||
///
|
||||
/// let barrier = Barrier::new(1);
|
||||
/// let barrier_wait_result = barrier.wait();
|
||||
/// println!("{:?}", barrier_wait_result.is_leader());
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[must_use]
|
||||
pub fn is_leader(&self) -> bool {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -1,422 +0,0 @@
|
||||
use super::once::OnceExclusiveState;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::Once;
|
||||
use crate::{fmt, ptr};
|
||||
|
||||
// We use the state of a Once as discriminant value. Upon creation, the state is
|
||||
// "incomplete" and `f` contains the initialization closure. In the first call to
|
||||
// `call_once`, `f` is taken and run. If it succeeds, `value` is set and the state
|
||||
// is changed to "complete". If it panics, the Once is poisoned, so none of the
|
||||
// two fields is initialized.
|
||||
union Data<T, F> {
|
||||
value: ManuallyDrop<T>,
|
||||
f: ManuallyDrop<F>,
|
||||
}
|
||||
|
||||
/// A value which is initialized on the first access.
|
||||
///
|
||||
/// This type is a thread-safe [`LazyCell`], and can be used in statics.
|
||||
/// Since initialization may be called from multiple threads, any
|
||||
/// dereferencing call will block the calling thread if another
|
||||
/// initialization routine is currently running.
|
||||
///
|
||||
/// [`LazyCell`]: crate::cell::LazyCell
|
||||
///
|
||||
/// # Poisoning
|
||||
///
|
||||
/// If the initialization closure passed to [`LazyLock::new`] panics, the lock will be poisoned.
|
||||
/// Once the lock is poisoned, any threads that attempt to access this lock (via a dereference
|
||||
/// or via an explicit call to [`force()`]) will panic.
|
||||
///
|
||||
/// This concept is similar to that of poisoning in the [`std::sync::poison`] module. A key
|
||||
/// difference, however, is that poisoning in `LazyLock` is _unrecoverable_. All future accesses of
|
||||
/// the lock from other threads will panic, whereas a type in [`std::sync::poison`] like
|
||||
/// [`std::sync::poison::Mutex`] allows recovery via [`PoisonError::into_inner()`].
|
||||
///
|
||||
/// [`force()`]: LazyLock::force
|
||||
/// [`std::sync::poison`]: crate::sync::poison
|
||||
/// [`std::sync::poison::Mutex`]: crate::sync::poison::Mutex
|
||||
/// [`PoisonError::into_inner()`]: crate::sync::poison::PoisonError::into_inner
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Initialize static variables with `LazyLock`.
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// // Note: static items do not call [`Drop`] on program termination, so this won't be deallocated.
|
||||
/// // this is fine, as the OS can deallocate the terminated program faster than we can free memory
|
||||
/// // but tools like valgrind might report "memory leaks" as it isn't obvious this is intentional.
|
||||
/// static DEEP_THOUGHT: LazyLock<String> = LazyLock::new(|| {
|
||||
/// # mod another_crate {
|
||||
/// # pub fn great_question() -> String { "42".to_string() }
|
||||
/// # }
|
||||
/// // M3 Ultra takes about 16 million years in --release config
|
||||
/// another_crate::great_question()
|
||||
/// });
|
||||
///
|
||||
/// // The `String` is built, stored in the `LazyLock`, and returned as `&String`.
|
||||
/// let _ = &*DEEP_THOUGHT;
|
||||
/// ```
|
||||
///
|
||||
/// Initialize fields with `LazyLock`.
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// struct UseCellLock {
|
||||
/// number: LazyLock<u32>,
|
||||
/// }
|
||||
/// fn main() {
|
||||
/// let lock: LazyLock<u32> = LazyLock::new(|| 0u32);
|
||||
///
|
||||
/// let data = UseCellLock { number: lock };
|
||||
/// println!("{}", *data.number);
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
pub struct LazyLock<T, F = fn() -> T> {
|
||||
// FIXME(nonpoison_once): if possible, switch to nonpoison version once it is available
|
||||
once: Once,
|
||||
data: UnsafeCell<Data<T, F>>,
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
||||
/// Creates a new lazy value with the given initializing function.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let hello = "Hello, World!".to_string();
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| hello.to_uppercase());
|
||||
///
|
||||
/// assert_eq!(&*lazy, "HELLO, WORLD!");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
#[rustc_const_stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
pub const fn new(f: F) -> LazyLock<T, F> {
|
||||
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
|
||||
}
|
||||
|
||||
/// Creates a new lazy value that is already initialized.
|
||||
#[inline]
|
||||
#[cfg(test)]
|
||||
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
|
||||
let once = Once::new();
|
||||
once.call_once(|| {});
|
||||
LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) }
|
||||
}
|
||||
|
||||
/// Consumes this `LazyLock` returning the stored value.
|
||||
///
|
||||
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the lock is poisoned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lazy_cell_into_inner)]
|
||||
///
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let hello = "Hello, World!".to_string();
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| hello.to_uppercase());
|
||||
///
|
||||
/// assert_eq!(&*lazy, "HELLO, WORLD!");
|
||||
/// assert_eq!(LazyLock::into_inner(lazy).ok(), Some("HELLO, WORLD!".to_string()));
|
||||
/// ```
|
||||
#[unstable(feature = "lazy_cell_into_inner", issue = "125623")]
|
||||
pub fn into_inner(mut this: Self) -> Result<T, F> {
|
||||
let state = this.once.state();
|
||||
match state {
|
||||
OnceExclusiveState::Poisoned => panic_poisoned(),
|
||||
state => {
|
||||
let this = ManuallyDrop::new(this);
|
||||
let data = unsafe { ptr::read(&this.data) }.into_inner();
|
||||
match state {
|
||||
OnceExclusiveState::Incomplete => {
|
||||
Err(ManuallyDrop::into_inner(unsafe { data.f }))
|
||||
}
|
||||
OnceExclusiveState::Complete => {
|
||||
Ok(ManuallyDrop::into_inner(unsafe { data.value }))
|
||||
}
|
||||
OnceExclusiveState::Poisoned => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Forces the evaluation of this lazy value and returns a mutable reference to
|
||||
/// the result.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let mut lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// let p = LazyLock::force_mut(&mut lazy);
|
||||
/// assert_eq!(*p, 92);
|
||||
/// *p = 44;
|
||||
/// assert_eq!(*lazy, 44);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
||||
pub fn force_mut(this: &mut LazyLock<T, F>) -> &mut T {
|
||||
#[cold]
|
||||
/// # Safety
|
||||
/// May only be called when the state is `Incomplete`.
|
||||
unsafe fn really_init_mut<T, F: FnOnce() -> T>(this: &mut LazyLock<T, F>) -> &mut T {
|
||||
struct PoisonOnPanic<'a, T, F>(&'a mut LazyLock<T, F>);
|
||||
impl<T, F> Drop for PoisonOnPanic<'_, T, F> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.0.once.set_state(OnceExclusiveState::Poisoned);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We always poison if the initializer panics (then we never check the data),
|
||||
// or set the data on success.
|
||||
let f = unsafe { ManuallyDrop::take(&mut this.data.get_mut().f) };
|
||||
// INVARIANT: Initiated from mutable reference, don't drop because we read it.
|
||||
let guard = PoisonOnPanic(this);
|
||||
let data = f();
|
||||
guard.0.data.get_mut().value = ManuallyDrop::new(data);
|
||||
guard.0.once.set_state(OnceExclusiveState::Complete);
|
||||
core::mem::forget(guard);
|
||||
// SAFETY: We put the value there above.
|
||||
unsafe { &mut this.data.get_mut().value }
|
||||
}
|
||||
|
||||
let state = this.once.state();
|
||||
match state {
|
||||
OnceExclusiveState::Poisoned => panic_poisoned(),
|
||||
// SAFETY: The `Once` states we completed the initialization.
|
||||
OnceExclusiveState::Complete => unsafe { &mut this.data.get_mut().value },
|
||||
// SAFETY: The state is `Incomplete`.
|
||||
OnceExclusiveState::Incomplete => unsafe { really_init_mut(this) },
|
||||
}
|
||||
}
|
||||
|
||||
/// Forces the evaluation of this lazy value and returns a reference to
|
||||
/// result. This is equivalent to the `Deref` impl, but is explicit.
|
||||
///
|
||||
/// This method will block the calling thread if another initialization
|
||||
/// routine is currently running.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// assert_eq!(LazyLock::force(&lazy), &92);
|
||||
/// assert_eq!(&*lazy, &92);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn force(this: &LazyLock<T, F>) -> &T {
|
||||
this.once.call_once_force(|state| {
|
||||
if state.is_poisoned() {
|
||||
panic_poisoned();
|
||||
}
|
||||
|
||||
// SAFETY: `call_once` only runs this closure once, ever.
|
||||
let data = unsafe { &mut *this.data.get() };
|
||||
let f = unsafe { ManuallyDrop::take(&mut data.f) };
|
||||
let value = f();
|
||||
data.value = ManuallyDrop::new(value);
|
||||
});
|
||||
|
||||
// SAFETY:
|
||||
// There are four possible scenarios:
|
||||
// * the closure was called and initialized `value`.
|
||||
// * the closure was called and panicked, so this point is never reached.
|
||||
// * the closure was not called, but a previous call initialized `value`.
|
||||
// * the closure was not called because the Once is poisoned, which we handled above.
|
||||
// So `value` has definitely been initialized and will not be modified again.
|
||||
unsafe { &*(*this.data.get()).value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F> LazyLock<T, F> {
|
||||
/// Returns a mutable reference to the value if initialized. Otherwise (if uninitialized or
|
||||
/// poisoned), returns `None`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let mut lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// assert_eq!(LazyLock::get_mut(&mut lazy), None);
|
||||
/// let _ = LazyLock::force(&lazy);
|
||||
/// *LazyLock::get_mut(&mut lazy).unwrap() = 44;
|
||||
/// assert_eq!(*lazy, 44);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
||||
pub fn get_mut(this: &mut LazyLock<T, F>) -> Option<&mut T> {
|
||||
// `state()` does not perform an atomic load, so prefer it over `is_complete()`.
|
||||
let state = this.once.state();
|
||||
match state {
|
||||
// SAFETY:
|
||||
// The closure has been run successfully, so `value` has been initialized.
|
||||
OnceExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the value if initialized. Otherwise (if uninitialized or poisoned),
|
||||
/// returns `None`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// assert_eq!(LazyLock::get(&lazy), None);
|
||||
/// let _ = LazyLock::force(&lazy);
|
||||
/// assert_eq!(LazyLock::get(&lazy), Some(&92));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "lazy_get", since = "1.94.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get(this: &LazyLock<T, F>) -> Option<&T> {
|
||||
if this.once.is_completed() {
|
||||
// SAFETY:
|
||||
// The closure has been run successfully, so `value` has been initialized
|
||||
// and will not be modified again.
|
||||
Some(unsafe { &(*this.data.get()).value })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T, F> Drop for LazyLock<T, F> {
|
||||
fn drop(&mut self) {
|
||||
match self.once.state() {
|
||||
OnceExclusiveState::Incomplete => unsafe {
|
||||
ManuallyDrop::drop(&mut self.data.get_mut().f)
|
||||
},
|
||||
OnceExclusiveState::Complete => unsafe {
|
||||
ManuallyDrop::drop(&mut self.data.get_mut().value)
|
||||
},
|
||||
OnceExclusiveState::Poisoned => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
|
||||
type Target = T;
|
||||
|
||||
/// Dereferences the value.
|
||||
///
|
||||
/// This method will block the calling thread if another initialization
|
||||
/// routine is currently running.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
LazyLock::force(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_deref_mut", since = "1.89.0")]
|
||||
impl<T, F: FnOnce() -> T> DerefMut for LazyLock<T, F> {
|
||||
/// # Panics
|
||||
///
|
||||
/// If the initialization closure panics (the one that is passed to the [`new()`] method), the
|
||||
/// panic is propagated to the caller, and the lock becomes poisoned. This will cause all future
|
||||
/// accesses of the lock (via [`force()`] or a dereference) to panic.
|
||||
///
|
||||
/// [`new()`]: LazyLock::new
|
||||
/// [`force()`]: LazyLock::force
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
LazyLock::force_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: Default> Default for LazyLock<T> {
|
||||
/// Creates a new lazy value using `Default` as the initializing function.
|
||||
#[inline]
|
||||
fn default() -> LazyLock<T> {
|
||||
LazyLock::new(T::default)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_tuple("LazyLock");
|
||||
match LazyLock::get(self) {
|
||||
Some(v) => d.field(v),
|
||||
None => d.field(&format_args!("<uninit>")),
|
||||
};
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
fn panic_poisoned() -> ! {
|
||||
panic!("LazyLock instance has previously been poisoned")
|
||||
}
|
||||
|
||||
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
|
||||
// to not impl `Sync` for `F`.
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
unsafe impl<T: Sync + Send, F: Send> Sync for LazyLock<T, F> {}
|
||||
// auto-derived `Send` impl is OK.
|
||||
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: RefUnwindSafe + UnwindSafe, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> {}
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
impl<T: UnwindSafe, F: UnwindSafe> UnwindSafe for LazyLock<T, F> {}
|
||||
@@ -1,569 +0,0 @@
|
||||
//! Bounded channel based on a preallocated array.
|
||||
//!
|
||||
//! This flavor has a fixed, positive capacity.
|
||||
//!
|
||||
//! The implementation is based on Dmitry Vyukov's bounded MPMC queue.
|
||||
//!
|
||||
//! Source:
|
||||
//! - <http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue>
|
||||
//! - <https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub>
|
||||
|
||||
use super::context::Context;
|
||||
use super::error::*;
|
||||
use super::select::{Operation, Selected, Token};
|
||||
use super::utils::{Backoff, CachePadded};
|
||||
use super::waker::SyncWaker;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{self, Atomic, AtomicUsize, Ordering};
|
||||
use crate::time::Instant;
|
||||
|
||||
/// A slot in a channel.
|
||||
struct Slot<T> {
|
||||
/// The current stamp.
|
||||
stamp: Atomic<usize>,
|
||||
|
||||
/// The message in this slot. Either read out in `read` or dropped through
|
||||
/// `discard_all_messages`.
|
||||
msg: UnsafeCell<MaybeUninit<T>>,
|
||||
}
|
||||
|
||||
/// The token type for the array flavor.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ArrayToken {
|
||||
/// Slot to read from or write to.
|
||||
slot: *const u8,
|
||||
|
||||
/// Stamp to store into the slot after reading or writing.
|
||||
stamp: usize,
|
||||
}
|
||||
|
||||
impl Default for ArrayToken {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ArrayToken { slot: ptr::null(), stamp: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// Bounded channel based on a preallocated array.
|
||||
pub(crate) struct Channel<T> {
|
||||
/// The head of the channel.
|
||||
///
|
||||
/// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
|
||||
/// packed into a single `usize`. The lower bits represent the index, while the upper bits
|
||||
/// represent the lap. The mark bit in the head is always zero.
|
||||
///
|
||||
/// Messages are popped from the head of the channel.
|
||||
head: CachePadded<Atomic<usize>>,
|
||||
|
||||
/// The tail of the channel.
|
||||
///
|
||||
/// This value is a "stamp" consisting of an index into the buffer, a mark bit, and a lap, but
|
||||
/// packed into a single `usize`. The lower bits represent the index, while the upper bits
|
||||
/// represent the lap. The mark bit indicates that the channel is disconnected.
|
||||
///
|
||||
/// Messages are pushed into the tail of the channel.
|
||||
tail: CachePadded<Atomic<usize>>,
|
||||
|
||||
/// The buffer holding slots.
|
||||
buffer: Box<[Slot<T>]>,
|
||||
|
||||
/// The channel capacity.
|
||||
cap: usize,
|
||||
|
||||
/// A stamp with the value of `{ lap: 1, mark: 0, index: 0 }`.
|
||||
one_lap: usize,
|
||||
|
||||
/// If this bit is set in the tail, that means the channel is disconnected.
|
||||
mark_bit: usize,
|
||||
|
||||
/// Senders waiting while the channel is full.
|
||||
senders: SyncWaker,
|
||||
|
||||
/// Receivers waiting while the channel is empty and not disconnected.
|
||||
receivers: SyncWaker,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
/// Creates a bounded channel of capacity `cap`.
|
||||
pub(crate) fn with_capacity(cap: usize) -> Self {
|
||||
assert!(cap > 0, "capacity must be positive");
|
||||
|
||||
// Compute constants `mark_bit` and `one_lap`.
|
||||
let mark_bit = (cap + 1).next_power_of_two();
|
||||
let one_lap = mark_bit * 2;
|
||||
|
||||
// Head is initialized to `{ lap: 0, mark: 0, index: 0 }`.
|
||||
let head = 0;
|
||||
// Tail is initialized to `{ lap: 0, mark: 0, index: 0 }`.
|
||||
let tail = 0;
|
||||
|
||||
// Allocate a buffer of `cap` slots initialized
|
||||
// with stamps.
|
||||
let buffer: Box<[Slot<T>]> = (0..cap)
|
||||
.map(|i| {
|
||||
// Set the stamp to `{ lap: 0, mark: 0, index: i }`.
|
||||
Slot { stamp: AtomicUsize::new(i), msg: UnsafeCell::new(MaybeUninit::uninit()) }
|
||||
})
|
||||
.collect();
|
||||
|
||||
Channel {
|
||||
buffer,
|
||||
cap,
|
||||
one_lap,
|
||||
mark_bit,
|
||||
head: CachePadded::new(AtomicUsize::new(head)),
|
||||
tail: CachePadded::new(AtomicUsize::new(tail)),
|
||||
senders: SyncWaker::new(),
|
||||
receivers: SyncWaker::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for sending a message.
|
||||
fn start_send(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut tail = self.tail.load(Ordering::Relaxed);
|
||||
|
||||
loop {
|
||||
// Check if the channel is disconnected.
|
||||
if tail & self.mark_bit != 0 {
|
||||
token.array.slot = ptr::null();
|
||||
token.array.stamp = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Deconstruct the tail.
|
||||
let index = tail & (self.mark_bit - 1);
|
||||
let lap = tail & !(self.one_lap - 1);
|
||||
|
||||
// Inspect the corresponding slot.
|
||||
debug_assert!(index < self.buffer.len());
|
||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
||||
|
||||
// If the tail and the stamp match, we may attempt to push.
|
||||
if tail == stamp {
|
||||
let new_tail = if index + 1 < self.cap {
|
||||
// Same lap, incremented index.
|
||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
||||
tail + 1
|
||||
} else {
|
||||
// One lap forward, index wraps around to zero.
|
||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
||||
lap.wrapping_add(self.one_lap)
|
||||
};
|
||||
|
||||
// Try moving the tail.
|
||||
match self.tail.compare_exchange_weak(
|
||||
tail,
|
||||
new_tail,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => {
|
||||
// Prepare the token for the follow-up call to `write`.
|
||||
token.array.slot = slot as *const Slot<T> as *const u8;
|
||||
token.array.stamp = tail + 1;
|
||||
return true;
|
||||
}
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
tail = self.tail.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
} else if stamp.wrapping_add(self.one_lap) == tail + 1 {
|
||||
atomic::fence(Ordering::SeqCst);
|
||||
let head = self.head.load(Ordering::Relaxed);
|
||||
|
||||
// If the head lags one lap behind the tail as well...
|
||||
if head.wrapping_add(self.one_lap) == tail {
|
||||
// ...then the channel is full.
|
||||
return false;
|
||||
}
|
||||
|
||||
backoff.spin_light();
|
||||
tail = self.tail.load(Ordering::Relaxed);
|
||||
} else {
|
||||
// Snooze because we need to wait for the stamp to get updated.
|
||||
backoff.spin_heavy();
|
||||
tail = self.tail.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message into the channel.
|
||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
||||
// If there is no slot, the channel is disconnected.
|
||||
if token.array.slot.is_null() {
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
// Write the message into the slot and update the stamp.
|
||||
unsafe {
|
||||
let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
|
||||
slot.msg.get().write(MaybeUninit::new(msg));
|
||||
slot.stamp.store(token.array.stamp, Ordering::Release);
|
||||
}
|
||||
|
||||
// Wake a sleeping receiver.
|
||||
self.receivers.notify();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for receiving a message.
|
||||
fn start_recv(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut head = self.head.load(Ordering::Relaxed);
|
||||
|
||||
loop {
|
||||
// Deconstruct the head.
|
||||
let index = head & (self.mark_bit - 1);
|
||||
let lap = head & !(self.one_lap - 1);
|
||||
|
||||
// Inspect the corresponding slot.
|
||||
debug_assert!(index < self.buffer.len());
|
||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
||||
|
||||
// If the stamp is ahead of the head by 1, we may attempt to pop.
|
||||
if head + 1 == stamp {
|
||||
let new = if index + 1 < self.cap {
|
||||
// Same lap, incremented index.
|
||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
||||
head + 1
|
||||
} else {
|
||||
// One lap forward, index wraps around to zero.
|
||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
||||
lap.wrapping_add(self.one_lap)
|
||||
};
|
||||
|
||||
// Try moving the head.
|
||||
match self.head.compare_exchange_weak(
|
||||
head,
|
||||
new,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => {
|
||||
// Prepare the token for the follow-up call to `read`.
|
||||
token.array.slot = slot as *const Slot<T> as *const u8;
|
||||
token.array.stamp = head.wrapping_add(self.one_lap);
|
||||
return true;
|
||||
}
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
head = self.head.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
} else if stamp == head {
|
||||
atomic::fence(Ordering::SeqCst);
|
||||
let tail = self.tail.load(Ordering::Relaxed);
|
||||
|
||||
// If the tail equals the head, that means the channel is empty.
|
||||
if (tail & !self.mark_bit) == head {
|
||||
// If the channel is disconnected...
|
||||
if tail & self.mark_bit != 0 {
|
||||
// ...then receive an error.
|
||||
token.array.slot = ptr::null();
|
||||
token.array.stamp = 0;
|
||||
return true;
|
||||
} else {
|
||||
// Otherwise, the receive operation is not ready.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
backoff.spin_light();
|
||||
head = self.head.load(Ordering::Relaxed);
|
||||
} else {
|
||||
// Snooze because we need to wait for the stamp to get updated.
|
||||
backoff.spin_heavy();
|
||||
head = self.head.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a message from the channel.
|
||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
||||
if token.array.slot.is_null() {
|
||||
// The channel is disconnected.
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Read the message from the slot and update the stamp.
|
||||
let msg = unsafe {
|
||||
let slot: &Slot<T> = &*(token.array.slot as *const Slot<T>);
|
||||
|
||||
let msg = slot.msg.get().read().assume_init();
|
||||
slot.stamp.store(token.array.stamp, Ordering::Release);
|
||||
msg
|
||||
};
|
||||
|
||||
// Wake a sleeping sender.
|
||||
self.senders.notify();
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
/// Attempts to send a message into the channel.
|
||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
let token = &mut Token::default();
|
||||
if self.start_send(token) {
|
||||
unsafe { self.write(token, msg).map_err(TrySendError::Disconnected) }
|
||||
} else {
|
||||
Err(TrySendError::Full(msg))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a message into the channel.
|
||||
pub(crate) fn send(
|
||||
&self,
|
||||
msg: T,
|
||||
deadline: Option<Instant>,
|
||||
) -> Result<(), SendTimeoutError<T>> {
|
||||
let token = &mut Token::default();
|
||||
loop {
|
||||
// Try sending a message.
|
||||
if self.start_send(token) {
|
||||
let res = unsafe { self.write(token, msg) };
|
||||
return res.map_err(SendTimeoutError::Disconnected);
|
||||
}
|
||||
|
||||
if let Some(d) = deadline {
|
||||
if Instant::now() >= d {
|
||||
return Err(SendTimeoutError::Timeout(msg));
|
||||
}
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a receiver wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
self.senders.register(oper, cx);
|
||||
|
||||
// Has the channel become ready just now?
|
||||
if !self.is_full() || self.is_disconnected() {
|
||||
let _ = cx.try_select(Selected::Aborted);
|
||||
}
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted | Selected::Disconnected => {
|
||||
self.senders.unregister(oper).unwrap();
|
||||
}
|
||||
Selected::Operation(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to receive a message without blocking.
|
||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
let token = &mut Token::default();
|
||||
|
||||
if self.start_recv(token) {
|
||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
||||
} else {
|
||||
Err(TryRecvError::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a message from the channel.
|
||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
||||
let token = &mut Token::default();
|
||||
loop {
|
||||
// Try receiving a message.
|
||||
if self.start_recv(token) {
|
||||
let res = unsafe { self.read(token) };
|
||||
return res.map_err(|_| RecvTimeoutError::Disconnected);
|
||||
}
|
||||
|
||||
if let Some(d) = deadline {
|
||||
if Instant::now() >= d {
|
||||
return Err(RecvTimeoutError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a sender wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
self.receivers.register(oper, cx);
|
||||
|
||||
// Has the channel become ready just now?
|
||||
if !self.is_empty() || self.is_disconnected() {
|
||||
let _ = cx.try_select(Selected::Aborted);
|
||||
}
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted | Selected::Disconnected => {
|
||||
self.receivers.unregister(oper).unwrap();
|
||||
// If the channel was disconnected, we still have to check for remaining
|
||||
// messages.
|
||||
}
|
||||
Selected::Operation(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current number of messages inside the channel.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
loop {
|
||||
// Load the tail, then load the head.
|
||||
let tail = self.tail.load(Ordering::SeqCst);
|
||||
let head = self.head.load(Ordering::SeqCst);
|
||||
|
||||
// If the tail didn't change, we've got consistent values to work with.
|
||||
if self.tail.load(Ordering::SeqCst) == tail {
|
||||
let hix = head & (self.mark_bit - 1);
|
||||
let tix = tail & (self.mark_bit - 1);
|
||||
|
||||
return if hix < tix {
|
||||
tix - hix
|
||||
} else if hix > tix {
|
||||
self.cap - hix + tix
|
||||
} else if (tail & !self.mark_bit) == head {
|
||||
0
|
||||
} else {
|
||||
self.cap
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the capacity of the channel.
|
||||
#[allow(clippy::unnecessary_wraps)] // This is intentional.
|
||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
||||
Some(self.cap)
|
||||
}
|
||||
|
||||
/// Disconnects senders and wakes up all blocked receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect_senders(&self) -> bool {
|
||||
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
|
||||
|
||||
if tail & self.mark_bit == 0 {
|
||||
self.receivers.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Disconnects receivers and wakes up all blocked senders.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
///
|
||||
/// # Safety
|
||||
/// May only be called once upon dropping the last receiver. The
|
||||
/// destruction of all other receivers must have been observed with acquire
|
||||
/// ordering or stronger.
|
||||
pub(crate) unsafe fn disconnect_receivers(&self) -> bool {
|
||||
let tail = self.tail.fetch_or(self.mark_bit, Ordering::SeqCst);
|
||||
let disconnected = if tail & self.mark_bit == 0 {
|
||||
self.senders.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
unsafe { self.discard_all_messages(tail) };
|
||||
disconnected
|
||||
}
|
||||
|
||||
/// Discards all messages.
|
||||
///
|
||||
/// `tail` should be the current (and therefore last) value of `tail`.
|
||||
///
|
||||
/// # Panicking
|
||||
/// If a destructor panics, the remaining messages are leaked, matching the
|
||||
/// behavior of the unbounded channel.
|
||||
///
|
||||
/// # Safety
|
||||
/// This method must only be called when dropping the last receiver. The
|
||||
/// destruction of all other receivers must have been observed with acquire
|
||||
/// ordering or stronger.
|
||||
unsafe fn discard_all_messages(&self, tail: usize) {
|
||||
debug_assert!(self.is_disconnected());
|
||||
|
||||
// Only receivers modify `head`, so since we are the last one,
|
||||
// this value will not change and will not be observed (since
|
||||
// no new messages can be sent after disconnection).
|
||||
let mut head = self.head.load(Ordering::Relaxed);
|
||||
let tail = tail & !self.mark_bit;
|
||||
|
||||
let backoff = Backoff::new();
|
||||
loop {
|
||||
// Deconstruct the head.
|
||||
let index = head & (self.mark_bit - 1);
|
||||
let lap = head & !(self.one_lap - 1);
|
||||
|
||||
// Inspect the corresponding slot.
|
||||
debug_assert!(index < self.buffer.len());
|
||||
let slot = unsafe { self.buffer.get_unchecked(index) };
|
||||
let stamp = slot.stamp.load(Ordering::Acquire);
|
||||
|
||||
// If the stamp is ahead of the head by 1, we may drop the message.
|
||||
if head + 1 == stamp {
|
||||
head = if index + 1 < self.cap {
|
||||
// Same lap, incremented index.
|
||||
// Set to `{ lap: lap, mark: 0, index: index + 1 }`.
|
||||
head + 1
|
||||
} else {
|
||||
// One lap forward, index wraps around to zero.
|
||||
// Set to `{ lap: lap.wrapping_add(1), mark: 0, index: 0 }`.
|
||||
lap.wrapping_add(self.one_lap)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(*slot.msg.get()).assume_init_drop();
|
||||
}
|
||||
// If the tail equals the head, that means the channel is empty.
|
||||
} else if tail == head {
|
||||
return;
|
||||
// Otherwise, a sender is about to write into the slot, so we need
|
||||
// to wait for it to update the stamp.
|
||||
} else {
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is disconnected.
|
||||
pub(crate) fn is_disconnected(&self) -> bool {
|
||||
self.tail.load(Ordering::SeqCst) & self.mark_bit != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is empty.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
let head = self.head.load(Ordering::SeqCst);
|
||||
let tail = self.tail.load(Ordering::SeqCst);
|
||||
|
||||
// Is the tail equal to the head?
|
||||
//
|
||||
// Note: If the head changes just before we load the tail, that means there was a moment
|
||||
// when the channel was not empty, so it is safe to just return `false`.
|
||||
(tail & !self.mark_bit) == head
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is full.
|
||||
pub(crate) fn is_full(&self) -> bool {
|
||||
let tail = self.tail.load(Ordering::SeqCst);
|
||||
let head = self.head.load(Ordering::SeqCst);
|
||||
|
||||
// Is the head lagging one lap behind tail?
|
||||
//
|
||||
// Note: If the tail changes just before we load the head, that means there was a moment
|
||||
// when the channel was not full, so it is safe to just return `false`.
|
||||
head.wrapping_add(self.one_lap) == tail & !self.mark_bit
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
//! Thread-local channel context.
|
||||
|
||||
use super::select::Selected;
|
||||
use super::waker::current_thread_id;
|
||||
use crate::cell::Cell;
|
||||
use crate::ptr;
|
||||
use alloc_crate::sync::Arc;
|
||||
use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering};
|
||||
use crate::thread::{self, Thread};
|
||||
use crate::time::Instant;
|
||||
|
||||
/// Thread-local context.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Context {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
/// Inner representation of `Context`.
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
/// Selected operation.
|
||||
select: Atomic<usize>,
|
||||
|
||||
/// A slot into which another thread may store a pointer to its `Packet`.
|
||||
packet: Atomic<*mut ()>,
|
||||
|
||||
/// Thread handle.
|
||||
thread: Thread,
|
||||
|
||||
/// Thread id.
|
||||
thread_id: usize,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Creates a new context for the duration of the closure.
|
||||
#[inline]
|
||||
pub fn with<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&Context) -> R,
|
||||
{
|
||||
thread_local! {
|
||||
/// Cached thread-local context.
|
||||
static CONTEXT: Cell<Option<Context>> = Cell::new(Some(Context::new()));
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
let mut f = |cx: &Context| -> R {
|
||||
let f = f.take().unwrap();
|
||||
f(cx)
|
||||
};
|
||||
|
||||
CONTEXT
|
||||
.try_with(|cell| match cell.take() {
|
||||
None => f(&Context::new()),
|
||||
Some(cx) => {
|
||||
cx.reset();
|
||||
let res = f(&cx);
|
||||
cell.set(Some(cx));
|
||||
res
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|_| f(&Context::new()))
|
||||
}
|
||||
|
||||
/// Creates a new `Context`.
|
||||
#[cold]
|
||||
fn new() -> Context {
|
||||
Context {
|
||||
inner: Arc::new(Inner {
|
||||
select: AtomicUsize::new(Selected::Waiting.into()),
|
||||
packet: AtomicPtr::new(ptr::null_mut()),
|
||||
thread: thread::current_or_unnamed(),
|
||||
thread_id: current_thread_id(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resets `select` and `packet`.
|
||||
#[inline]
|
||||
fn reset(&self) {
|
||||
self.inner.select.store(Selected::Waiting.into(), Ordering::Release);
|
||||
self.inner.packet.store(ptr::null_mut(), Ordering::Release);
|
||||
}
|
||||
|
||||
/// Attempts to select an operation.
|
||||
///
|
||||
/// On failure, the previously selected operation is returned.
|
||||
#[inline]
|
||||
pub fn try_select(&self, select: Selected) -> Result<(), Selected> {
|
||||
self.inner
|
||||
.select
|
||||
.compare_exchange(
|
||||
Selected::Waiting.into(),
|
||||
select.into(),
|
||||
Ordering::AcqRel,
|
||||
Ordering::Acquire,
|
||||
)
|
||||
.map(|_| ())
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Stores a packet.
|
||||
///
|
||||
/// This method must be called after `try_select` succeeds and there is a packet to provide.
|
||||
#[inline]
|
||||
pub fn store_packet(&self, packet: *mut ()) {
|
||||
if !packet.is_null() {
|
||||
self.inner.packet.store(packet, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits until an operation is selected and returns it.
|
||||
///
|
||||
/// If the deadline is reached, `Selected::Aborted` will be selected.
|
||||
///
|
||||
/// # Safety
|
||||
/// This may only be called from the thread this `Context` belongs to.
|
||||
#[inline]
|
||||
pub unsafe fn wait_until(&self, deadline: Option<Instant>) -> Selected {
|
||||
loop {
|
||||
// Check whether an operation has been selected.
|
||||
let sel = Selected::from(self.inner.select.load(Ordering::Acquire));
|
||||
if sel != Selected::Waiting {
|
||||
return sel;
|
||||
}
|
||||
|
||||
// If there's a deadline, park the current thread until the deadline is reached.
|
||||
if let Some(end) = deadline {
|
||||
let now = Instant::now();
|
||||
|
||||
if now < end {
|
||||
// SAFETY: guaranteed by caller.
|
||||
unsafe { self.inner.thread.park_timeout(end - now) };
|
||||
} else {
|
||||
// The deadline has been reached. Try aborting select.
|
||||
return match self.try_select(Selected::Aborted) {
|
||||
Ok(()) => Selected::Aborted,
|
||||
Err(s) => s,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// SAFETY: guaranteed by caller.
|
||||
unsafe { self.inner.thread.park() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unparks the thread this context belongs to.
|
||||
#[inline]
|
||||
pub fn unpark(&self) {
|
||||
self.inner.thread.unpark();
|
||||
}
|
||||
|
||||
/// Returns the id of the thread this context belongs to.
|
||||
#[inline]
|
||||
pub fn thread_id(&self) -> usize {
|
||||
self.inner.thread_id
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering};
|
||||
use crate::{ops, process};
|
||||
|
||||
/// Reference counter internals.
|
||||
struct Counter<C> {
|
||||
/// The number of senders associated with the channel.
|
||||
senders: Atomic<usize>,
|
||||
|
||||
/// The number of receivers associated with the channel.
|
||||
receivers: Atomic<usize>,
|
||||
|
||||
/// Set to `true` if the last sender or the last receiver reference deallocates the channel.
|
||||
destroy: Atomic<bool>,
|
||||
|
||||
/// The internal channel.
|
||||
chan: C,
|
||||
}
|
||||
|
||||
/// Wraps a channel into the reference counter.
|
||||
pub(crate) fn new<C>(chan: C) -> (Sender<C>, Receiver<C>) {
|
||||
let counter = Box::into_raw(Box::new(Counter {
|
||||
senders: AtomicUsize::new(1),
|
||||
receivers: AtomicUsize::new(1),
|
||||
destroy: AtomicBool::new(false),
|
||||
chan,
|
||||
}));
|
||||
let s = Sender { counter };
|
||||
let r = Receiver { counter };
|
||||
(s, r)
|
||||
}
|
||||
|
||||
/// The sending side.
|
||||
pub(crate) struct Sender<C> {
|
||||
counter: *mut Counter<C>,
|
||||
}
|
||||
|
||||
impl<C> Sender<C> {
|
||||
/// Returns the internal `Counter`.
|
||||
fn counter(&self) -> &Counter<C> {
|
||||
unsafe { &*self.counter }
|
||||
}
|
||||
|
||||
/// Acquires another sender reference.
|
||||
pub(crate) fn acquire(&self) -> Sender<C> {
|
||||
let count = self.counter().senders.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
// Cloning senders and calling `mem::forget` on the clones could potentially overflow the
|
||||
// counter. It's very difficult to recover sensibly from such degenerate scenarios so we
|
||||
// just abort when the count becomes very large.
|
||||
if count > isize::MAX as usize {
|
||||
process::abort();
|
||||
}
|
||||
|
||||
Sender { counter: self.counter }
|
||||
}
|
||||
|
||||
/// Releases the sender reference.
|
||||
///
|
||||
/// Function `disconnect` will be called if this is the last sender reference.
|
||||
pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
|
||||
if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||
disconnect(&self.counter().chan);
|
||||
|
||||
if self.counter().destroy.swap(true, Ordering::AcqRel) {
|
||||
drop(unsafe { Box::from_raw(self.counter) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> ops::Deref for Sender<C> {
|
||||
type Target = C;
|
||||
|
||||
fn deref(&self) -> &C {
|
||||
&self.counter().chan
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> PartialEq for Sender<C> {
|
||||
fn eq(&self, other: &Sender<C>) -> bool {
|
||||
self.counter == other.counter
|
||||
}
|
||||
}
|
||||
|
||||
/// The receiving side.
|
||||
pub(crate) struct Receiver<C> {
|
||||
counter: *mut Counter<C>,
|
||||
}
|
||||
|
||||
impl<C> Receiver<C> {
|
||||
/// Returns the internal `Counter`.
|
||||
fn counter(&self) -> &Counter<C> {
|
||||
unsafe { &*self.counter }
|
||||
}
|
||||
|
||||
/// Acquires another receiver reference.
|
||||
pub(crate) fn acquire(&self) -> Receiver<C> {
|
||||
let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
// Cloning receivers and calling `mem::forget` on the clones could potentially overflow the
|
||||
// counter. It's very difficult to recover sensibly from such degenerate scenarios so we
|
||||
// just abort when the count becomes very large.
|
||||
if count > isize::MAX as usize {
|
||||
process::abort();
|
||||
}
|
||||
|
||||
Receiver { counter: self.counter }
|
||||
}
|
||||
|
||||
/// Releases the receiver reference.
|
||||
///
|
||||
/// Function `disconnect` will be called if this is the last receiver reference.
|
||||
pub(crate) unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
|
||||
if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||
disconnect(&self.counter().chan);
|
||||
|
||||
if self.counter().destroy.swap(true, Ordering::AcqRel) {
|
||||
drop(unsafe { Box::from_raw(self.counter) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> ops::Deref for Receiver<C> {
|
||||
type Target = C;
|
||||
|
||||
fn deref(&self) -> &C {
|
||||
&self.counter().chan
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> PartialEq for Receiver<C> {
|
||||
fn eq(&self, other: &Receiver<C>) -> bool {
|
||||
self.counter == other.counter
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
pub use crate::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError, TrySendError};
|
||||
use crate::{error, fmt};
|
||||
|
||||
/// An error returned from the [`send_timeout`] method.
|
||||
///
|
||||
/// The error contains the message being sent so it can be recovered.
|
||||
///
|
||||
/// [`send_timeout`]: super::Sender::send_timeout
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
pub enum SendTimeoutError<T> {
|
||||
/// The message could not be sent because the channel is full and the operation timed out.
|
||||
///
|
||||
/// If this is a zero-capacity channel, then the error indicates that there was no receiver
|
||||
/// available to receive the message and the operation timed out.
|
||||
Timeout(T),
|
||||
|
||||
/// The message could not be sent because the channel is disconnected.
|
||||
Disconnected(T),
|
||||
}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> fmt::Debug for SendTimeoutError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"SendTimeoutError(..)".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> fmt::Display for SendTimeoutError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
SendTimeoutError::Timeout(..) => "timed out waiting on send operation".fmt(f),
|
||||
SendTimeoutError::Disconnected(..) => "sending on a disconnected channel".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> error::Error for SendTimeoutError<T> {}
|
||||
|
||||
#[unstable(feature = "mpmc_channel", issue = "126840")]
|
||||
impl<T> From<SendError<T>> for SendTimeoutError<T> {
|
||||
fn from(err: SendError<T>) -> SendTimeoutError<T> {
|
||||
match err {
|
||||
SendError(e) => SendTimeoutError::Disconnected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,668 +0,0 @@
|
||||
//! Unbounded channel implemented as a linked list.
|
||||
|
||||
use super::context::Context;
|
||||
use super::error::*;
|
||||
use super::select::{Operation, Selected, Token};
|
||||
use super::utils::{Backoff, CachePadded};
|
||||
use super::waker::SyncWaker;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{self, Atomic, AtomicPtr, AtomicUsize, Ordering};
|
||||
use crate::time::Instant;
|
||||
|
||||
// Bits indicating the state of a slot:
|
||||
// * If a message has been written into the slot, `WRITE` is set.
|
||||
// * If a message has been read from the slot, `READ` is set.
|
||||
// * If the block is being destroyed, `DESTROY` is set.
|
||||
const WRITE: usize = 1;
|
||||
const READ: usize = 2;
|
||||
const DESTROY: usize = 4;
|
||||
|
||||
// Each block covers one "lap" of indices.
|
||||
const LAP: usize = 32;
|
||||
// The maximum number of messages a block can hold.
|
||||
const BLOCK_CAP: usize = LAP - 1;
|
||||
// How many lower bits are reserved for metadata.
|
||||
const SHIFT: usize = 1;
|
||||
// Has two different purposes:
|
||||
// * If set in head, indicates that the block is not the last one.
|
||||
// * If set in tail, indicates that the channel is disconnected.
|
||||
const MARK_BIT: usize = 1;
|
||||
|
||||
/// A slot in a block.
|
||||
struct Slot<T> {
|
||||
/// The message.
|
||||
msg: UnsafeCell<MaybeUninit<T>>,
|
||||
|
||||
/// The state of the slot.
|
||||
state: Atomic<usize>,
|
||||
}
|
||||
|
||||
impl<T> Slot<T> {
|
||||
/// Waits until a message is written into the slot.
|
||||
fn wait_write(&self) {
|
||||
let backoff = Backoff::new();
|
||||
while self.state.load(Ordering::Acquire) & WRITE == 0 {
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A block in a linked list.
|
||||
///
|
||||
/// Each block in the list can hold up to `BLOCK_CAP` messages.
|
||||
struct Block<T> {
|
||||
/// The next block in the linked list.
|
||||
next: Atomic<*mut Block<T>>,
|
||||
|
||||
/// Slots for messages.
|
||||
slots: [Slot<T>; BLOCK_CAP],
|
||||
}
|
||||
|
||||
impl<T> Block<T> {
|
||||
/// Creates an empty block.
|
||||
fn new() -> Box<Block<T>> {
|
||||
// SAFETY: This is safe because:
|
||||
// [1] `Block::next` (Atomic<*mut _>) may be safely zero initialized.
|
||||
// [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4].
|
||||
// [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it
|
||||
// holds a MaybeUninit.
|
||||
// [4] `Slot::state` (Atomic<usize>) may be safely zero initialized.
|
||||
unsafe { Box::new_zeroed().assume_init() }
|
||||
}
|
||||
|
||||
/// Waits until the next pointer is set.
|
||||
fn wait_next(&self) -> *mut Block<T> {
|
||||
let backoff = Backoff::new();
|
||||
loop {
|
||||
let next = self.next.load(Ordering::Acquire);
|
||||
if !next.is_null() {
|
||||
return next;
|
||||
}
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the `DESTROY` bit in slots starting from `start` and destroys the block.
|
||||
unsafe fn destroy(this: *mut Block<T>, start: usize) {
|
||||
// It is not necessary to set the `DESTROY` bit in the last slot because that slot has
|
||||
// begun destruction of the block.
|
||||
for i in start..BLOCK_CAP - 1 {
|
||||
let slot = unsafe { (*this).slots.get_unchecked(i) };
|
||||
|
||||
// Mark the `DESTROY` bit if a thread is still using the slot.
|
||||
if slot.state.load(Ordering::Acquire) & READ == 0
|
||||
&& slot.state.fetch_or(DESTROY, Ordering::AcqRel) & READ == 0
|
||||
{
|
||||
// If a thread is still using the slot, it will continue destruction of the block.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No thread is using the block, now it is safe to destroy it.
|
||||
drop(unsafe { Box::from_raw(this) });
|
||||
}
|
||||
}
|
||||
|
||||
/// A position in a channel.
|
||||
#[derive(Debug)]
|
||||
struct Position<T> {
|
||||
/// The index in the channel.
|
||||
index: Atomic<usize>,
|
||||
|
||||
/// The block in the linked list.
|
||||
block: Atomic<*mut Block<T>>,
|
||||
}
|
||||
|
||||
/// The token type for the list flavor.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ListToken {
|
||||
/// The block of slots.
|
||||
block: *const u8,
|
||||
|
||||
/// The offset into the block.
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl Default for ListToken {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ListToken { block: ptr::null(), offset: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
/// Unbounded channel implemented as a linked list.
|
||||
///
|
||||
/// Each message sent into the channel is assigned a sequence number, i.e. an index. Indices are
|
||||
/// represented as numbers of type `usize` and wrap on overflow.
|
||||
///
|
||||
/// Consecutive messages are grouped into blocks in order to put less pressure on the allocator and
|
||||
/// improve cache efficiency.
|
||||
pub(crate) struct Channel<T> {
|
||||
/// The head of the channel.
|
||||
head: CachePadded<Position<T>>,
|
||||
|
||||
/// The tail of the channel.
|
||||
tail: CachePadded<Position<T>>,
|
||||
|
||||
/// Receivers waiting while the channel is empty and not disconnected.
|
||||
receivers: SyncWaker,
|
||||
|
||||
/// Indicates that dropping a `Channel<T>` may drop messages of type `T`.
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
/// Creates a new unbounded channel.
|
||||
pub(crate) fn new() -> Self {
|
||||
Channel {
|
||||
head: CachePadded::new(Position {
|
||||
block: AtomicPtr::new(ptr::null_mut()),
|
||||
index: AtomicUsize::new(0),
|
||||
}),
|
||||
tail: CachePadded::new(Position {
|
||||
block: AtomicPtr::new(ptr::null_mut()),
|
||||
index: AtomicUsize::new(0),
|
||||
}),
|
||||
receivers: SyncWaker::new(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for sending a message.
|
||||
fn start_send(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut tail = self.tail.index.load(Ordering::Acquire);
|
||||
let mut block = self.tail.block.load(Ordering::Acquire);
|
||||
let mut next_block = None;
|
||||
|
||||
loop {
|
||||
// Check if the channel is disconnected.
|
||||
if tail & MARK_BIT != 0 {
|
||||
token.list.block = ptr::null();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate the offset of the index into the block.
|
||||
let offset = (tail >> SHIFT) % LAP;
|
||||
|
||||
// If we reached the end of the block, wait until the next one is installed.
|
||||
if offset == BLOCK_CAP {
|
||||
backoff.spin_heavy();
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
block = self.tail.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're going to have to install the next block, allocate it in advance in order to
|
||||
// make the wait for other threads as short as possible.
|
||||
if offset + 1 == BLOCK_CAP && next_block.is_none() {
|
||||
next_block = Some(Block::<T>::new());
|
||||
}
|
||||
|
||||
// If this is the first message to be sent into the channel, we need to allocate the
|
||||
// first block and install it.
|
||||
if block.is_null() {
|
||||
let new = Box::into_raw(Block::<T>::new());
|
||||
|
||||
if self
|
||||
.tail
|
||||
.block
|
||||
.compare_exchange(block, new, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
// This yield point leaves the channel in a half-initialized state where the
|
||||
// tail.block pointer is set but the head.block is not. This is used to
|
||||
// facilitate the test in src/tools/miri/tests/pass/issues/issue-139553.rs
|
||||
#[cfg(miri)]
|
||||
crate::thread::yield_now();
|
||||
self.head.block.store(new, Ordering::Release);
|
||||
block = new;
|
||||
} else {
|
||||
next_block = unsafe { Some(Box::from_raw(new)) };
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
block = self.tail.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let new_tail = tail + (1 << SHIFT);
|
||||
|
||||
// Try advancing the tail forward.
|
||||
match self.tail.index.compare_exchange_weak(
|
||||
tail,
|
||||
new_tail,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Acquire,
|
||||
) {
|
||||
Ok(_) => unsafe {
|
||||
// If we've reached the end of the block, install the next one.
|
||||
if offset + 1 == BLOCK_CAP {
|
||||
let next_block = Box::into_raw(next_block.unwrap());
|
||||
self.tail.block.store(next_block, Ordering::Release);
|
||||
self.tail.index.fetch_add(1 << SHIFT, Ordering::Release);
|
||||
(*block).next.store(next_block, Ordering::Release);
|
||||
}
|
||||
|
||||
token.list.block = block as *const u8;
|
||||
token.list.offset = offset;
|
||||
return true;
|
||||
},
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
block = self.tail.block.load(Ordering::Acquire);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message into the channel.
|
||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
||||
// If there is no slot, the channel is disconnected.
|
||||
if token.list.block.is_null() {
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
// Write the message into the slot.
|
||||
let block = token.list.block as *mut Block<T>;
|
||||
let offset = token.list.offset;
|
||||
unsafe {
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
slot.msg.get().write(MaybeUninit::new(msg));
|
||||
slot.state.fetch_or(WRITE, Ordering::Release);
|
||||
}
|
||||
|
||||
// Wake a sleeping receiver.
|
||||
self.receivers.notify();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Attempts to reserve a slot for receiving a message.
|
||||
fn start_recv(&self, token: &mut Token) -> bool {
|
||||
let backoff = Backoff::new();
|
||||
let mut head = self.head.index.load(Ordering::Acquire);
|
||||
let mut block = self.head.block.load(Ordering::Acquire);
|
||||
|
||||
loop {
|
||||
// Calculate the offset of the index into the block.
|
||||
let offset = (head >> SHIFT) % LAP;
|
||||
|
||||
// If we reached the end of the block, wait until the next one is installed.
|
||||
if offset == BLOCK_CAP {
|
||||
backoff.spin_heavy();
|
||||
head = self.head.index.load(Ordering::Acquire);
|
||||
block = self.head.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut new_head = head + (1 << SHIFT);
|
||||
|
||||
if new_head & MARK_BIT == 0 {
|
||||
atomic::fence(Ordering::SeqCst);
|
||||
let tail = self.tail.index.load(Ordering::Relaxed);
|
||||
|
||||
// If the tail equals the head, that means the channel is empty.
|
||||
if head >> SHIFT == tail >> SHIFT {
|
||||
// If the channel is disconnected...
|
||||
if tail & MARK_BIT != 0 {
|
||||
// ...then receive an error.
|
||||
token.list.block = ptr::null();
|
||||
return true;
|
||||
} else {
|
||||
// Otherwise, the receive operation is not ready.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If head and tail are not in the same block, set `MARK_BIT` in head.
|
||||
if (head >> SHIFT) / LAP != (tail >> SHIFT) / LAP {
|
||||
new_head |= MARK_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// The block can be null here only if the first message is being sent into the channel.
|
||||
// In that case, just wait until it gets initialized.
|
||||
if block.is_null() {
|
||||
backoff.spin_heavy();
|
||||
head = self.head.index.load(Ordering::Acquire);
|
||||
block = self.head.block.load(Ordering::Acquire);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try moving the head index forward.
|
||||
match self.head.index.compare_exchange_weak(
|
||||
head,
|
||||
new_head,
|
||||
Ordering::SeqCst,
|
||||
Ordering::Acquire,
|
||||
) {
|
||||
Ok(_) => unsafe {
|
||||
// If we've reached the end of the block, move to the next one.
|
||||
if offset + 1 == BLOCK_CAP {
|
||||
let next = (*block).wait_next();
|
||||
let mut next_index = (new_head & !MARK_BIT).wrapping_add(1 << SHIFT);
|
||||
if !(*next).next.load(Ordering::Relaxed).is_null() {
|
||||
next_index |= MARK_BIT;
|
||||
}
|
||||
|
||||
self.head.block.store(next, Ordering::Release);
|
||||
self.head.index.store(next_index, Ordering::Release);
|
||||
}
|
||||
|
||||
token.list.block = block as *const u8;
|
||||
token.list.offset = offset;
|
||||
return true;
|
||||
},
|
||||
Err(_) => {
|
||||
backoff.spin_light();
|
||||
head = self.head.index.load(Ordering::Acquire);
|
||||
block = self.head.block.load(Ordering::Acquire);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a message from the channel.
|
||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
||||
if token.list.block.is_null() {
|
||||
// The channel is disconnected.
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Read the message.
|
||||
let block = token.list.block as *mut Block<T>;
|
||||
let offset = token.list.offset;
|
||||
unsafe {
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
slot.wait_write();
|
||||
let msg = slot.msg.get().read().assume_init();
|
||||
|
||||
// Destroy the block if we've reached the end, or if another thread wanted to destroy but
|
||||
// couldn't because we were busy reading from the slot.
|
||||
if offset + 1 == BLOCK_CAP {
|
||||
Block::destroy(block, 0);
|
||||
} else if slot.state.fetch_or(READ, Ordering::AcqRel) & DESTROY != 0 {
|
||||
Block::destroy(block, offset + 1);
|
||||
}
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to send a message into the channel.
|
||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
self.send(msg, None).map_err(|err| match err {
|
||||
SendTimeoutError::Disconnected(msg) => TrySendError::Disconnected(msg),
|
||||
SendTimeoutError::Timeout(_) => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Sends a message into the channel.
|
||||
pub(crate) fn send(
|
||||
&self,
|
||||
msg: T,
|
||||
_deadline: Option<Instant>,
|
||||
) -> Result<(), SendTimeoutError<T>> {
|
||||
let token = &mut Token::default();
|
||||
assert!(self.start_send(token));
|
||||
unsafe { self.write(token, msg).map_err(SendTimeoutError::Disconnected) }
|
||||
}
|
||||
|
||||
/// Attempts to receive a message without blocking.
|
||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
let token = &mut Token::default();
|
||||
|
||||
if self.start_recv(token) {
|
||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
||||
} else {
|
||||
Err(TryRecvError::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a message from the channel.
|
||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
||||
let token = &mut Token::default();
|
||||
loop {
|
||||
if self.start_recv(token) {
|
||||
unsafe {
|
||||
return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(d) = deadline {
|
||||
if Instant::now() >= d {
|
||||
return Err(RecvTimeoutError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare for blocking until a sender wakes us up.
|
||||
Context::with(|cx| {
|
||||
let oper = Operation::hook(token);
|
||||
self.receivers.register(oper, cx);
|
||||
|
||||
// Has the channel become ready just now?
|
||||
if !self.is_empty() || self.is_disconnected() {
|
||||
let _ = cx.try_select(Selected::Aborted);
|
||||
}
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted | Selected::Disconnected => {
|
||||
self.receivers.unregister(oper).unwrap();
|
||||
// If the channel was disconnected, we still have to check for remaining
|
||||
// messages.
|
||||
}
|
||||
Selected::Operation(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current number of messages inside the channel.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
loop {
|
||||
// Load the tail index, then load the head index.
|
||||
let mut tail = self.tail.index.load(Ordering::SeqCst);
|
||||
let mut head = self.head.index.load(Ordering::SeqCst);
|
||||
|
||||
// If the tail index didn't change, we've got consistent indices to work with.
|
||||
if self.tail.index.load(Ordering::SeqCst) == tail {
|
||||
// Erase the lower bits.
|
||||
tail &= !((1 << SHIFT) - 1);
|
||||
head &= !((1 << SHIFT) - 1);
|
||||
|
||||
// Fix up indices if they fall onto block ends.
|
||||
if (tail >> SHIFT) & (LAP - 1) == LAP - 1 {
|
||||
tail = tail.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
if (head >> SHIFT) & (LAP - 1) == LAP - 1 {
|
||||
head = head.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
|
||||
// Rotate indices so that head falls into the first block.
|
||||
let lap = (head >> SHIFT) / LAP;
|
||||
tail = tail.wrapping_sub((lap * LAP) << SHIFT);
|
||||
head = head.wrapping_sub((lap * LAP) << SHIFT);
|
||||
|
||||
// Remove the lower bits.
|
||||
tail >>= SHIFT;
|
||||
head >>= SHIFT;
|
||||
|
||||
// Return the difference minus the number of blocks between tail and head.
|
||||
return tail - head - tail / LAP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the capacity of the channel.
|
||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Disconnects senders and wakes up all blocked receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect_senders(&self) -> bool {
|
||||
let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
|
||||
|
||||
if tail & MARK_BIT == 0 {
|
||||
self.receivers.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Disconnects receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect_receivers(&self) -> bool {
|
||||
let tail = self.tail.index.fetch_or(MARK_BIT, Ordering::SeqCst);
|
||||
|
||||
if tail & MARK_BIT == 0 {
|
||||
// If receivers are dropped first, discard all messages to free
|
||||
// memory eagerly.
|
||||
self.discard_all_messages();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Discards all messages.
|
||||
///
|
||||
/// This method should only be called when all receivers are dropped.
|
||||
fn discard_all_messages(&self) {
|
||||
let backoff = Backoff::new();
|
||||
let mut tail = self.tail.index.load(Ordering::Acquire);
|
||||
loop {
|
||||
let offset = (tail >> SHIFT) % LAP;
|
||||
if offset != BLOCK_CAP {
|
||||
break;
|
||||
}
|
||||
|
||||
// New updates to tail will be rejected by MARK_BIT and aborted unless it's
|
||||
// at boundary. We need to wait for the updates take affect otherwise there
|
||||
// can be memory leaks.
|
||||
backoff.spin_heavy();
|
||||
tail = self.tail.index.load(Ordering::Acquire);
|
||||
}
|
||||
|
||||
let mut head = self.head.index.load(Ordering::Acquire);
|
||||
// The channel may be uninitialized, so we have to swap to avoid overwriting any sender's attempts
|
||||
// to initialize the first block before noticing that the receivers disconnected. Late allocations
|
||||
// will be deallocated by the sender in Drop.
|
||||
let mut block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||
|
||||
// If we're going to be dropping messages we need to synchronize with initialization
|
||||
if head >> SHIFT != tail >> SHIFT {
|
||||
// The block can be null here only if a sender is in the process of initializing the
|
||||
// channel while another sender managed to send a message by inserting it into the
|
||||
// semi-initialized channel and advanced the tail.
|
||||
// In that case, just wait until it gets initialized.
|
||||
while block.is_null() {
|
||||
backoff.spin_heavy();
|
||||
block = self.head.block.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||
}
|
||||
}
|
||||
// After this point `head.block` is not modified again and it will be deallocated if it's
|
||||
// non-null. The `Drop` code of the channel, which runs after this function, also attempts
|
||||
// to deallocate `head.block` if it's non-null. Therefore this function must maintain the
|
||||
// invariant that if a deallocation of head.block is attempted then it must also be set to
|
||||
// NULL. Failing to do so will lead to the Drop code attempting a double free. For this
|
||||
// reason both reads above do an atomic swap instead of a simple atomic load.
|
||||
|
||||
unsafe {
|
||||
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
|
||||
while head >> SHIFT != tail >> SHIFT {
|
||||
let offset = (head >> SHIFT) % LAP;
|
||||
|
||||
if offset < BLOCK_CAP {
|
||||
// Drop the message in the slot.
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
slot.wait_write();
|
||||
let p = &mut *slot.msg.get();
|
||||
p.as_mut_ptr().drop_in_place();
|
||||
} else {
|
||||
(*block).wait_next();
|
||||
// Deallocate the block and move to the next one.
|
||||
let next = (*block).next.load(Ordering::Acquire);
|
||||
drop(Box::from_raw(block));
|
||||
block = next;
|
||||
}
|
||||
|
||||
head = head.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
|
||||
// Deallocate the last remaining block.
|
||||
if !block.is_null() {
|
||||
drop(Box::from_raw(block));
|
||||
}
|
||||
}
|
||||
|
||||
head &= !MARK_BIT;
|
||||
self.head.index.store(head, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is disconnected.
|
||||
pub(crate) fn is_disconnected(&self) -> bool {
|
||||
self.tail.index.load(Ordering::SeqCst) & MARK_BIT != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is empty.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
let head = self.head.index.load(Ordering::SeqCst);
|
||||
let tail = self.tail.index.load(Ordering::SeqCst);
|
||||
head >> SHIFT == tail >> SHIFT
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is full.
|
||||
pub(crate) fn is_full(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Channel<T> {
|
||||
fn drop(&mut self) {
|
||||
let mut head = self.head.index.load(Ordering::Relaxed);
|
||||
let mut tail = self.tail.index.load(Ordering::Relaxed);
|
||||
let mut block = self.head.block.load(Ordering::Relaxed);
|
||||
|
||||
// Erase the lower bits.
|
||||
head &= !((1 << SHIFT) - 1);
|
||||
tail &= !((1 << SHIFT) - 1);
|
||||
|
||||
unsafe {
|
||||
// Drop all messages between head and tail and deallocate the heap-allocated blocks.
|
||||
while head != tail {
|
||||
let offset = (head >> SHIFT) % LAP;
|
||||
|
||||
if offset < BLOCK_CAP {
|
||||
// Drop the message in the slot.
|
||||
let slot = (*block).slots.get_unchecked(offset);
|
||||
let p = &mut *slot.msg.get();
|
||||
p.as_mut_ptr().drop_in_place();
|
||||
} else {
|
||||
// Deallocate the block and move to the next one.
|
||||
let next = (*block).next.load(Ordering::Relaxed);
|
||||
drop(Box::from_raw(block));
|
||||
block = next;
|
||||
}
|
||||
|
||||
head = head.wrapping_add(1 << SHIFT);
|
||||
}
|
||||
|
||||
// Deallocate the last remaining block.
|
||||
if !block.is_null() {
|
||||
drop(Box::from_raw(block));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,71 +0,0 @@
|
||||
/// Temporary data that gets initialized during a blocking operation, and is consumed by
|
||||
/// `read` or `write`.
|
||||
///
|
||||
/// Each field contains data associated with a specific channel flavor.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Token {
|
||||
pub(crate) array: super::array::ArrayToken,
|
||||
pub(crate) list: super::list::ListToken,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) zero: super::zero::ZeroToken,
|
||||
}
|
||||
|
||||
/// Identifier associated with an operation by a specific thread on a specific channel.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Operation(usize);
|
||||
|
||||
impl Operation {
|
||||
/// Creates an operation identifier from a mutable reference.
|
||||
///
|
||||
/// This function essentially just turns the address of the reference into a number. The
|
||||
/// reference should point to a variable that is specific to the thread and the operation,
|
||||
/// and is alive for the entire duration of a blocking operation.
|
||||
#[inline]
|
||||
pub fn hook<T>(r: &mut T) -> Operation {
|
||||
let val = r as *mut T as usize;
|
||||
// Make sure that the pointer address doesn't equal the numerical representation of
|
||||
// `Selected::{Waiting, Aborted, Disconnected}`.
|
||||
assert!(val > 2);
|
||||
Operation(val)
|
||||
}
|
||||
}
|
||||
|
||||
/// Current state of a blocking operation.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Selected {
|
||||
/// Still waiting for an operation.
|
||||
Waiting,
|
||||
|
||||
/// The attempt to block the current thread has been aborted.
|
||||
Aborted,
|
||||
|
||||
/// An operation became ready because a channel is disconnected.
|
||||
Disconnected,
|
||||
|
||||
/// An operation became ready because a message can be sent or received.
|
||||
Operation(Operation),
|
||||
}
|
||||
|
||||
impl From<usize> for Selected {
|
||||
#[inline]
|
||||
fn from(val: usize) -> Selected {
|
||||
match val {
|
||||
0 => Selected::Waiting,
|
||||
1 => Selected::Aborted,
|
||||
2 => Selected::Disconnected,
|
||||
oper => Selected::Operation(Operation(oper)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<usize> for Selected {
|
||||
#[inline]
|
||||
fn into(self) -> usize {
|
||||
match self {
|
||||
Selected::Waiting => 0,
|
||||
Selected::Aborted => 1,
|
||||
Selected::Disconnected => 2,
|
||||
Selected::Operation(Operation(val)) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Ensure that thread_local init with `const { 0 }` still has unique address at run-time
|
||||
#[test]
|
||||
fn waker_current_thread_id() {
|
||||
let first = super::waker::current_thread_id();
|
||||
let t = crate::thread::spawn(move || {
|
||||
let second = super::waker::current_thread_id();
|
||||
assert_ne!(first, second);
|
||||
assert_eq!(second, super::waker::current_thread_id());
|
||||
});
|
||||
|
||||
assert_eq!(first, super::waker::current_thread_id());
|
||||
t.join().unwrap();
|
||||
assert_eq!(first, super::waker::current_thread_id());
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
use crate::cell::Cell;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
|
||||
/// Pads and aligns a value to the length of a cache line.
|
||||
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
|
||||
// Starting from Intel's Sandy Bridge, spatial prefetcher is now pulling pairs of 64-byte cache
|
||||
// lines at a time, so we have to align to 128 bytes rather than 64.
|
||||
//
|
||||
// Sources:
|
||||
// - https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
|
||||
// - https://github.com/facebook/folly/blob/1b5288e6eea6df074758f877c849b6e73bbb9fbb/folly/lang/Align.h#L107
|
||||
//
|
||||
// ARM's big.LITTLE architecture has asymmetric cores and "big" cores have 128-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://www.mono-project.com/news/2016/09/12/arm64-icache/
|
||||
//
|
||||
// powerpc64 has 128-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_ppc64x.go#L9
|
||||
#[cfg_attr(
|
||||
any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "powerpc64",),
|
||||
repr(align(128))
|
||||
)]
|
||||
// arm, mips and mips64 have 32-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_arm.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mipsle.go#L7
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_mips64x.go#L9
|
||||
#[cfg_attr(
|
||||
any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "mips64",
|
||||
target_arch = "mips64r6",
|
||||
),
|
||||
repr(align(32))
|
||||
)]
|
||||
// s390x has 256-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_s390x.go#L7
|
||||
#[cfg_attr(target_arch = "s390x", repr(align(256)))]
|
||||
// x86, wasm and riscv have 64-byte cache line size.
|
||||
//
|
||||
// Sources:
|
||||
// - https://github.com/golang/go/blob/dda2991c2ea0c5914714469c4defc2562a907230/src/internal/cpu/cpu_x86.go#L9
|
||||
// - https://github.com/golang/go/blob/3dd58676054223962cd915bb0934d1f9f489d4d2/src/internal/cpu/cpu_wasm.go#L7
|
||||
// - https://github.com/golang/go/blob/5e31f78c8a4ed1b872ddc194f0cd1ae931b37d7e/src/internal/cpu/cpu_riscv64.go#L7
|
||||
//
|
||||
// All others are assumed to have 64-byte cache line size.
|
||||
#[cfg_attr(
|
||||
not(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "mips64",
|
||||
target_arch = "mips64r6",
|
||||
target_arch = "s390x",
|
||||
)),
|
||||
repr(align(64))
|
||||
)]
|
||||
pub struct CachePadded<T> {
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<T> CachePadded<T> {
|
||||
/// Pads and aligns a value to the length of a cache line.
|
||||
pub fn new(value: T) -> CachePadded<T> {
|
||||
CachePadded::<T> { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for CachePadded<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for CachePadded<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
const SPIN_LIMIT: u32 = 6;
|
||||
|
||||
/// Performs quadratic backoff in spin loops.
|
||||
pub struct Backoff {
|
||||
step: Cell<u32>,
|
||||
}
|
||||
|
||||
impl Backoff {
|
||||
/// Creates a new `Backoff`.
|
||||
pub fn new() -> Self {
|
||||
Backoff { step: Cell::new(0) }
|
||||
}
|
||||
|
||||
/// Backs off using lightweight spinning.
|
||||
///
|
||||
/// This method should be used for retrying an operation because another thread made
|
||||
/// progress. i.e. on CAS failure.
|
||||
#[inline]
|
||||
pub fn spin_light(&self) {
|
||||
let step = self.step.get().min(SPIN_LIMIT);
|
||||
for _ in 0..step.pow(2) {
|
||||
crate::hint::spin_loop();
|
||||
}
|
||||
|
||||
self.step.set(self.step.get() + 1);
|
||||
}
|
||||
|
||||
/// Backs off using heavyweight spinning.
|
||||
///
|
||||
/// This method should be used in blocking loops where parking the thread is not an option.
|
||||
#[inline]
|
||||
pub fn spin_heavy(&self) {
|
||||
if self.step.get() <= SPIN_LIMIT {
|
||||
for _ in 0..self.step.get().pow(2) {
|
||||
crate::hint::spin_loop()
|
||||
}
|
||||
} else {
|
||||
crate::thread::yield_now();
|
||||
}
|
||||
|
||||
self.step.set(self.step.get() + 1);
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
//! Waking mechanism for threads blocked on channel operations.
|
||||
|
||||
use super::context::Context;
|
||||
use super::select::{Operation, Selected};
|
||||
use crate::ptr;
|
||||
use crate::sync::Mutex;
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
||||
|
||||
/// Represents a thread blocked on a specific channel operation.
|
||||
pub(crate) struct Entry {
|
||||
/// The operation.
|
||||
pub(crate) oper: Operation,
|
||||
|
||||
/// Optional packet.
|
||||
pub(crate) packet: *mut (),
|
||||
|
||||
/// Context associated with the thread owning this operation.
|
||||
pub(crate) cx: Context,
|
||||
}
|
||||
|
||||
/// A queue of threads blocked on channel operations.
|
||||
///
|
||||
/// This data structure is used by threads to register blocking operations and get woken up once
|
||||
/// an operation becomes ready.
|
||||
pub(crate) struct Waker {
|
||||
/// A list of select operations.
|
||||
selectors: Vec<Entry>,
|
||||
|
||||
/// A list of operations waiting to be ready.
|
||||
observers: Vec<Entry>,
|
||||
}
|
||||
|
||||
impl Waker {
|
||||
/// Creates a new `Waker`.
|
||||
#[inline]
|
||||
pub(crate) fn new() -> Self {
|
||||
Waker { selectors: Vec::new(), observers: Vec::new() }
|
||||
}
|
||||
|
||||
/// Registers a select operation.
|
||||
#[inline]
|
||||
pub(crate) fn register(&mut self, oper: Operation, cx: &Context) {
|
||||
self.register_with_packet(oper, ptr::null_mut(), cx);
|
||||
}
|
||||
|
||||
/// Registers a select operation and a packet.
|
||||
#[inline]
|
||||
pub(crate) fn register_with_packet(&mut self, oper: Operation, packet: *mut (), cx: &Context) {
|
||||
self.selectors.push(Entry { oper, packet, cx: cx.clone() });
|
||||
}
|
||||
|
||||
/// Unregisters a select operation.
|
||||
#[inline]
|
||||
pub(crate) fn unregister(&mut self, oper: Operation) -> Option<Entry> {
|
||||
if let Some((i, _)) =
|
||||
self.selectors.iter().enumerate().find(|&(_, entry)| entry.oper == oper)
|
||||
{
|
||||
let entry = self.selectors.remove(i);
|
||||
Some(entry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to find another thread's entry, select the operation, and wake it up.
|
||||
#[inline]
|
||||
pub(crate) fn try_select(&mut self) -> Option<Entry> {
|
||||
if self.selectors.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let thread_id = current_thread_id();
|
||||
|
||||
self.selectors
|
||||
.iter()
|
||||
.position(|selector| {
|
||||
// Does the entry belong to a different thread?
|
||||
selector.cx.thread_id() != thread_id
|
||||
&& selector // Try selecting this operation.
|
||||
.cx
|
||||
.try_select(Selected::Operation(selector.oper))
|
||||
.is_ok()
|
||||
&& {
|
||||
// Provide the packet.
|
||||
selector.cx.store_packet(selector.packet);
|
||||
// Wake the thread up.
|
||||
selector.cx.unpark();
|
||||
true
|
||||
}
|
||||
})
|
||||
// Remove the entry from the queue to keep it clean and improve
|
||||
// performance.
|
||||
.map(|pos| self.selectors.remove(pos))
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies all operations waiting to be ready.
|
||||
#[inline]
|
||||
pub(crate) fn notify(&mut self) {
|
||||
for entry in self.observers.drain(..) {
|
||||
if entry.cx.try_select(Selected::Operation(entry.oper)).is_ok() {
|
||||
entry.cx.unpark();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies all registered operations that the channel is disconnected.
|
||||
#[inline]
|
||||
pub(crate) fn disconnect(&mut self) {
|
||||
for entry in self.selectors.iter() {
|
||||
if entry.cx.try_select(Selected::Disconnected).is_ok() {
|
||||
// Wake the thread up.
|
||||
//
|
||||
// Here we don't remove the entry from the queue. Registered threads must
|
||||
// unregister from the waker by themselves. They might also want to recover the
|
||||
// packet value and destroy it, if necessary.
|
||||
entry.cx.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
self.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Waker {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
debug_assert_eq!(self.selectors.len(), 0);
|
||||
debug_assert_eq!(self.observers.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// A waker that can be shared among threads without locking.
|
||||
///
|
||||
/// This is a simple wrapper around `Waker` that internally uses a mutex for synchronization.
|
||||
pub(crate) struct SyncWaker {
|
||||
/// The inner `Waker`.
|
||||
inner: Mutex<Waker>,
|
||||
|
||||
/// `true` if the waker is empty.
|
||||
is_empty: Atomic<bool>,
|
||||
}
|
||||
|
||||
impl SyncWaker {
|
||||
/// Creates a new `SyncWaker`.
|
||||
#[inline]
|
||||
pub(crate) fn new() -> Self {
|
||||
SyncWaker { inner: Mutex::new(Waker::new()), is_empty: AtomicBool::new(true) }
|
||||
}
|
||||
|
||||
/// Registers the current thread with an operation.
|
||||
#[inline]
|
||||
pub(crate) fn register(&self, oper: Operation, cx: &Context) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.register(oper, cx);
|
||||
self.is_empty
|
||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Unregisters an operation previously registered by the current thread.
|
||||
#[inline]
|
||||
pub(crate) fn unregister(&self, oper: Operation) -> Option<Entry> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let entry = inner.unregister(oper);
|
||||
self.is_empty
|
||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
||||
entry
|
||||
}
|
||||
|
||||
/// Attempts to find one thread (not the current one), select its operation, and wake it up.
|
||||
#[inline]
|
||||
pub(crate) fn notify(&self) {
|
||||
if !self.is_empty.load(Ordering::SeqCst) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
if !self.is_empty.load(Ordering::SeqCst) {
|
||||
inner.try_select();
|
||||
inner.notify();
|
||||
self.is_empty.store(
|
||||
inner.selectors.is_empty() && inner.observers.is_empty(),
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Notifies all threads that the channel is disconnected.
|
||||
#[inline]
|
||||
pub(crate) fn disconnect(&self) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.disconnect();
|
||||
self.is_empty
|
||||
.store(inner.selectors.is_empty() && inner.observers.is_empty(), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SyncWaker {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.is_empty.load(Ordering::SeqCst));
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a unique id for the current thread.
|
||||
#[inline]
|
||||
pub fn current_thread_id() -> usize {
|
||||
// `u8` is not drop so this variable will be available during thread destruction,
|
||||
// whereas `thread::current()` would not be
|
||||
thread_local! { static DUMMY: u8 = const { 0 } }
|
||||
DUMMY.with(|x| (x as *const u8).addr())
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
//! Zero-capacity channel.
|
||||
//!
|
||||
//! This kind of channel is also known as *rendezvous* channel.
|
||||
|
||||
use super::context::Context;
|
||||
use super::error::*;
|
||||
use super::select::{Operation, Selected, Token};
|
||||
use super::utils::Backoff;
|
||||
use super::waker::Waker;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::sync::Mutex;
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
||||
use crate::time::Instant;
|
||||
use crate::{fmt, ptr};
|
||||
|
||||
/// A pointer to a packet.
|
||||
pub(crate) struct ZeroToken(*mut ());
|
||||
|
||||
impl Default for ZeroToken {
|
||||
fn default() -> Self {
|
||||
Self(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ZeroToken {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&(self.0 as usize), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A slot for passing one message from a sender to a receiver.
|
||||
struct Packet<T> {
|
||||
/// Equals `true` if the packet is allocated on the stack.
|
||||
on_stack: bool,
|
||||
|
||||
/// Equals `true` once the packet is ready for reading or writing.
|
||||
ready: Atomic<bool>,
|
||||
|
||||
/// The message.
|
||||
msg: UnsafeCell<Option<T>>,
|
||||
}
|
||||
|
||||
impl<T> Packet<T> {
|
||||
/// Creates an empty packet on the stack.
|
||||
fn empty_on_stack() -> Packet<T> {
|
||||
Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(None) }
|
||||
}
|
||||
|
||||
/// Creates a packet on the stack, containing a message.
|
||||
fn message_on_stack(msg: T) -> Packet<T> {
|
||||
Packet { on_stack: true, ready: AtomicBool::new(false), msg: UnsafeCell::new(Some(msg)) }
|
||||
}
|
||||
|
||||
/// Waits until the packet becomes ready for reading or writing.
|
||||
fn wait_ready(&self) {
|
||||
let backoff = Backoff::new();
|
||||
while !self.ready.load(Ordering::Acquire) {
|
||||
backoff.spin_heavy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner representation of a zero-capacity channel.
|
||||
struct Inner {
|
||||
/// Senders waiting to pair up with a receive operation.
|
||||
senders: Waker,
|
||||
|
||||
/// Receivers waiting to pair up with a send operation.
|
||||
receivers: Waker,
|
||||
|
||||
/// Equals `true` when the channel is disconnected.
|
||||
is_disconnected: bool,
|
||||
}
|
||||
|
||||
/// Zero-capacity channel.
|
||||
pub(crate) struct Channel<T> {
|
||||
/// Inner representation of the channel.
|
||||
inner: Mutex<Inner>,
|
||||
|
||||
/// Indicates that dropping a `Channel<T>` may drop values of type `T`.
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
/// Constructs a new zero-capacity channel.
|
||||
pub(crate) fn new() -> Self {
|
||||
Channel {
|
||||
inner: Mutex::new(Inner {
|
||||
senders: Waker::new(),
|
||||
receivers: Waker::new(),
|
||||
is_disconnected: false,
|
||||
}),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a message into the packet.
|
||||
pub(crate) unsafe fn write(&self, token: &mut Token, msg: T) -> Result<(), T> {
|
||||
// If there is no packet, the channel is disconnected.
|
||||
if token.zero.0.is_null() {
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let packet = &*(token.zero.0 as *const Packet<T>);
|
||||
packet.msg.get().write(Some(msg));
|
||||
packet.ready.store(true, Ordering::Release);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a message from the packet.
|
||||
pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<T, ()> {
|
||||
// If there is no packet, the channel is disconnected.
|
||||
if token.zero.0.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let packet = unsafe { &*(token.zero.0 as *const Packet<T>) };
|
||||
|
||||
if packet.on_stack {
|
||||
// The message has been in the packet from the beginning, so there is no need to wait
|
||||
// for it. However, after reading the message, we need to set `ready` to `true` in
|
||||
// order to signal that the packet can be destroyed.
|
||||
let msg = unsafe { packet.msg.get().replace(None) }.unwrap();
|
||||
packet.ready.store(true, Ordering::Release);
|
||||
Ok(msg)
|
||||
} else {
|
||||
// Wait until the message becomes available, then read it and destroy the
|
||||
// heap-allocated packet.
|
||||
packet.wait_ready();
|
||||
unsafe {
|
||||
let msg = packet.msg.get().replace(None).unwrap();
|
||||
drop(Box::from_raw(token.zero.0 as *mut Packet<T>));
|
||||
Ok(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to send a message into the channel.
|
||||
pub(crate) fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting receiver, pair up with it.
|
||||
if let Some(operation) = inner.receivers.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe {
|
||||
self.write(token, msg).ok().unwrap();
|
||||
}
|
||||
Ok(())
|
||||
} else if inner.is_disconnected {
|
||||
Err(TrySendError::Disconnected(msg))
|
||||
} else {
|
||||
Err(TrySendError::Full(msg))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a message into the channel.
|
||||
pub(crate) fn send(
|
||||
&self,
|
||||
msg: T,
|
||||
deadline: Option<Instant>,
|
||||
) -> Result<(), SendTimeoutError<T>> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting receiver, pair up with it.
|
||||
if let Some(operation) = inner.receivers.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe {
|
||||
self.write(token, msg).ok().unwrap();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if inner.is_disconnected {
|
||||
return Err(SendTimeoutError::Disconnected(msg));
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a receiver wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
let mut packet = Packet::<T>::message_on_stack(msg);
|
||||
inner.senders.register_with_packet(oper, (&raw mut packet) as *mut (), cx);
|
||||
inner.receivers.notify();
|
||||
drop(inner);
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted => {
|
||||
self.inner.lock().unwrap().senders.unregister(oper).unwrap();
|
||||
let msg = unsafe { packet.msg.get().replace(None).unwrap() };
|
||||
Err(SendTimeoutError::Timeout(msg))
|
||||
}
|
||||
Selected::Disconnected => {
|
||||
self.inner.lock().unwrap().senders.unregister(oper).unwrap();
|
||||
let msg = unsafe { packet.msg.get().replace(None).unwrap() };
|
||||
Err(SendTimeoutError::Disconnected(msg))
|
||||
}
|
||||
Selected::Operation(_) => {
|
||||
// Wait until the message is read, then drop the packet.
|
||||
packet.wait_ready();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Attempts to receive a message without blocking.
|
||||
pub(crate) fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting sender, pair up with it.
|
||||
if let Some(operation) = inner.senders.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe { self.read(token).map_err(|_| TryRecvError::Disconnected) }
|
||||
} else if inner.is_disconnected {
|
||||
Err(TryRecvError::Disconnected)
|
||||
} else {
|
||||
Err(TryRecvError::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receives a message from the channel.
|
||||
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
|
||||
let token = &mut Token::default();
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
// If there's a waiting sender, pair up with it.
|
||||
if let Some(operation) = inner.senders.try_select() {
|
||||
token.zero.0 = operation.packet;
|
||||
drop(inner);
|
||||
unsafe {
|
||||
return self.read(token).map_err(|_| RecvTimeoutError::Disconnected);
|
||||
}
|
||||
}
|
||||
|
||||
if inner.is_disconnected {
|
||||
return Err(RecvTimeoutError::Disconnected);
|
||||
}
|
||||
|
||||
Context::with(|cx| {
|
||||
// Prepare for blocking until a sender wakes us up.
|
||||
let oper = Operation::hook(token);
|
||||
let mut packet = Packet::<T>::empty_on_stack();
|
||||
inner.receivers.register_with_packet(oper, (&raw mut packet) as *mut (), cx);
|
||||
inner.senders.notify();
|
||||
drop(inner);
|
||||
|
||||
// Block the current thread.
|
||||
// SAFETY: the context belongs to the current thread.
|
||||
let sel = unsafe { cx.wait_until(deadline) };
|
||||
|
||||
match sel {
|
||||
Selected::Waiting => unreachable!(),
|
||||
Selected::Aborted => {
|
||||
self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
|
||||
Err(RecvTimeoutError::Timeout)
|
||||
}
|
||||
Selected::Disconnected => {
|
||||
self.inner.lock().unwrap().receivers.unregister(oper).unwrap();
|
||||
Err(RecvTimeoutError::Disconnected)
|
||||
}
|
||||
Selected::Operation(_) => {
|
||||
// Wait until the message is provided, then read it.
|
||||
packet.wait_ready();
|
||||
unsafe { Ok(packet.msg.get().replace(None).unwrap()) }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Disconnects the channel and wakes up all blocked senders and receivers.
|
||||
///
|
||||
/// Returns `true` if this call disconnected the channel.
|
||||
pub(crate) fn disconnect(&self) -> bool {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
if !inner.is_disconnected {
|
||||
inner.is_disconnected = true;
|
||||
inner.senders.disconnect();
|
||||
inner.receivers.disconnect();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current number of messages inside the channel.
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
/// Returns the capacity of the channel.
|
||||
#[allow(clippy::unnecessary_wraps)] // This is intentional.
|
||||
pub(crate) fn capacity(&self) -> Option<usize> {
|
||||
Some(0)
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is empty.
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns `true` if the channel is full.
|
||||
pub(crate) fn is_full(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,45 +0,0 @@
|
||||
//! Non-poisoning synchronous locks.
|
||||
//!
|
||||
//! The difference from the locks in the [`poison`] module is that the locks in this module will not
|
||||
//! become poisoned when a thread panics while holding a guard.
|
||||
//!
|
||||
//! [`poison`]: super::poison
|
||||
|
||||
use crate::fmt;
|
||||
|
||||
/// A type alias for the result of a nonblocking locking method.
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
pub type TryLockResult<Guard> = Result<Guard, WouldBlock>;
|
||||
|
||||
/// A lock could not be acquired at this time because the operation would otherwise block.
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
pub struct WouldBlock;
|
||||
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
impl fmt::Debug for WouldBlock {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"WouldBlock".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "sync_nonpoison", issue = "134645")]
|
||||
impl fmt::Display for WouldBlock {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"try_lock failed because the operation would block".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub use self::condvar::Condvar;
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::mutex::MappedMutexGuard;
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub use self::mutex::{Mutex, MutexGuard};
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
||||
#[unstable(feature = "nonpoison_rwlock", issue = "134645")]
|
||||
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
mod condvar;
|
||||
mod mutex;
|
||||
mod rwlock;
|
||||
@@ -1,444 +0,0 @@
|
||||
use crate::fmt;
|
||||
use crate::ops::DerefMut;
|
||||
use crate::sync::WaitTimeoutResult;
|
||||
use crate::sync::nonpoison::{MutexGuard, mutex};
|
||||
use crate::sys::sync as sys;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
/// A Condition Variable
|
||||
///
|
||||
/// For more information about condition variables, check out the documentation for the poisoning
|
||||
/// variant of this type at [`poison::Condvar`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Note that this `Condvar` does **not** propagate information about threads that panic while
|
||||
/// holding a lock. If you need this functionality, see [`poison::Mutex`] and [`poison::Condvar`].
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`poison::Mutex`]: crate::sync::poison::Mutex
|
||||
/// [`poison::Condvar`]: crate::sync::poison::Condvar
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub struct Condvar {
|
||||
inner: sys::Condvar,
|
||||
}
|
||||
|
||||
impl Condvar {
|
||||
/// Creates a new condition variable which is ready to be waited on and
|
||||
/// notified.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Condvar;
|
||||
///
|
||||
/// let condvar = Condvar::new();
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn new() -> Condvar {
|
||||
Condvar { inner: sys::Condvar::new() }
|
||||
}
|
||||
|
||||
/// Blocks the current thread until this condition variable receives a
|
||||
/// notification.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// Note that this function is susceptible to spurious wakeups. Condition
|
||||
/// variables normally have a boolean predicate associated with them, and
|
||||
/// the predicate must always be checked each time this function returns to
|
||||
/// protect against spurious wakeups.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may [`panic!`] if it is used with more than one mutex
|
||||
/// over time.
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait<T>(&self, guard: &mut MutexGuard<'_, T>) {
|
||||
unsafe {
|
||||
let lock = mutex::guard_lock(guard);
|
||||
self.inner.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until the provided condition becomes false.
|
||||
///
|
||||
/// `condition` is checked immediately; if not met (returns `true`), this
|
||||
/// will [`wait`] for the next notification then check again. This repeats
|
||||
/// until `condition` returns `false`, in which case this function returns.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
|
||||
/// let mut guard = lock.lock();
|
||||
/// cvar.wait_while(&mut guard, |pending| { *pending });
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_while<T, F>(&self, guard: &mut MutexGuard<'_, T>, mut condition: F)
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
while condition(guard.deref_mut()) {
|
||||
self.wait(guard);
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`] except that
|
||||
/// the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time. This function is susceptible to spurious wakeups.
|
||||
/// Condition variables normally have a boolean predicate associated with
|
||||
/// them, and the predicate must always be checked each time this function
|
||||
/// returns to protect against spurious wakeups. Furthermore, since the timeout
|
||||
/// is given relative to the moment this function is called, it needs to be adjusted
|
||||
/// when this function is called in a loop. The [`wait_timeout_while`] method
|
||||
/// lets you wait with a timeout while a predicate is true, taking care of all these concerns.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will have been re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout_while`]: Self::wait_timeout_while
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // as long as the value inside the `Mutex<bool>` is `false`, we wait
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout(&mut started, Duration::from_millis(10));
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_timeout<T>(
|
||||
&self,
|
||||
guard: &mut MutexGuard<'_, T>,
|
||||
dur: Duration,
|
||||
) -> WaitTimeoutResult {
|
||||
let success = unsafe {
|
||||
let lock = mutex::guard_lock(guard);
|
||||
self.inner.wait_timeout(lock, dur)
|
||||
};
|
||||
WaitTimeoutResult(!success)
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait_while`] except
|
||||
/// that the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed without the condition being met.
|
||||
///
|
||||
/// Like [`wait_while`], the lock specified will have been re-acquired when this
|
||||
/// function returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait_while`]: Self::wait_while
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut guard = lock.lock();
|
||||
/// let result = cvar.wait_timeout_while(
|
||||
/// &mut guard,
|
||||
/// Duration::from_millis(100),
|
||||
/// |&mut pending| pending,
|
||||
/// );
|
||||
/// if result.timed_out() {
|
||||
/// // timed-out without the condition ever evaluating to false.
|
||||
/// }
|
||||
/// // access the locked mutex via guard
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_timeout_while<T, F>(
|
||||
&self,
|
||||
guard: &mut MutexGuard<'_, T>,
|
||||
dur: Duration,
|
||||
mut condition: F,
|
||||
) -> WaitTimeoutResult
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
let start = Instant::now();
|
||||
|
||||
while condition(guard.deref_mut()) {
|
||||
let timeout = match dur.checked_sub(start.elapsed()) {
|
||||
Some(timeout) => timeout,
|
||||
None => return WaitTimeoutResult(true),
|
||||
};
|
||||
|
||||
self.wait_timeout(guard, timeout);
|
||||
}
|
||||
|
||||
WaitTimeoutResult(false)
|
||||
}
|
||||
|
||||
/// Wakes up one blocked thread on this condvar.
|
||||
///
|
||||
/// If there is a blocked thread on this condition variable, then it will
|
||||
/// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
|
||||
/// `notify_one` are not buffered in any way.
|
||||
///
|
||||
/// To wake up all threads, see [`notify_all`].
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn notify_one(&self) {
|
||||
self.inner.notify_one()
|
||||
}
|
||||
|
||||
/// Wakes up all blocked threads on this condvar.
|
||||
///
|
||||
/// This method will ensure that any current waiters on the condition
|
||||
/// variable are awoken. Calls to `notify_all()` are not buffered in any
|
||||
/// way.
|
||||
///
|
||||
/// To wake up only one thread, see [`notify_one`].
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(nonpoison_condvar)]
|
||||
///
|
||||
/// use std::sync::nonpoison::{Mutex, Condvar};
|
||||
/// use std::sync::Arc;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_all();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn notify_all(&self) {
|
||||
self.inner.notify_all()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
impl fmt::Debug for Condvar {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Condvar").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
impl Default for Condvar {
|
||||
/// Creates a `Condvar` which is ready to be waited on and notified.
|
||||
fn default() -> Condvar {
|
||||
Condvar::new()
|
||||
}
|
||||
}
|
||||
@@ -1,649 +0,0 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::nonpoison::{TryLockResult, WouldBlock};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A mutual exclusion primitive useful for protecting shared data that does not keep track of
|
||||
/// lock poisoning.
|
||||
///
|
||||
/// For more information about mutexes, check out the documentation for the poisoning variant of
|
||||
/// this lock at [`poison::Mutex`].
|
||||
///
|
||||
/// [`poison::Mutex`]: crate::sync::poison::Mutex
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Note that this `Mutex` does **not** propagate threads that panic while holding the lock via
|
||||
/// poisoning. If you need this functionality, see [`poison::Mutex`].
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::thread;
|
||||
/// use std::sync::{Arc, nonpoison::Mutex};
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0u32));
|
||||
/// let mut handles = Vec::new();
|
||||
///
|
||||
/// for n in 0..10 {
|
||||
/// let m = Arc::clone(&mutex);
|
||||
/// let handle = thread::spawn(move || {
|
||||
/// let mut guard = m.lock();
|
||||
/// *guard += 1;
|
||||
/// panic!("panic from thread {n} {guard}")
|
||||
/// });
|
||||
/// handles.push(handle);
|
||||
/// }
|
||||
///
|
||||
/// for h in handles {
|
||||
/// let _ = h.join();
|
||||
/// }
|
||||
///
|
||||
/// println!("Finished, locked {} times", mutex.lock());
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutex")]
|
||||
pub struct Mutex<T: ?Sized> {
|
||||
inner: sys::Mutex,
|
||||
data: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire
|
||||
/// the owned `T` from the `Mutex` via [`into_inner`].
|
||||
///
|
||||
/// [`into_inner`]: Mutex::into_inner
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
|
||||
|
||||
/// `T` must be `Send` for [`Mutex`] to be `Sync`.
|
||||
/// This ensures that the protected data can be accessed safely from multiple threads
|
||||
/// without causing data races or other unsafe behavior.
|
||||
///
|
||||
/// [`Mutex<T>`] provides mutable access to `T` to one thread at a time. However, it's essential
|
||||
/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in
|
||||
/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer,
|
||||
/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap
|
||||
/// allocation with a non-atomic reference count. If we were to use `Mutex<Rc<_>>`, it would
|
||||
/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable
|
||||
/// to potential data races.
|
||||
///
|
||||
/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available
|
||||
/// to one thread at a time if `T` is not `Sync`.
|
||||
///
|
||||
/// [`Rc`]: crate::rc::Rc
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
|
||||
|
||||
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
|
||||
/// dropped (falls out of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] and [`DerefMut`] implementations.
|
||||
///
|
||||
/// This structure is created by the [`lock`] and [`try_lock`] methods on
|
||||
/// [`Mutex`].
|
||||
///
|
||||
/// [`lock`]: Mutex::lock
|
||||
/// [`try_lock`]: Mutex::try_lock
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
#[must_not_suspend = "holding a MutexGuard across suspend \
|
||||
points can cause deadlocks, delays, \
|
||||
and cause Futures to not implement `Send`"]
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[clippy::has_significant_drop]
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutexGuard")]
|
||||
pub struct MutexGuard<'a, T: ?Sized + 'a> {
|
||||
lock: &'a Mutex<T>,
|
||||
}
|
||||
|
||||
/// A [`MutexGuard`] is not `Send` to maximize platform portability.
|
||||
///
|
||||
/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to
|
||||
/// release mutex locks on the same thread they were acquired.
|
||||
/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from
|
||||
/// another thread.
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
|
||||
|
||||
/// `T` must be `Sync` for a [`MutexGuard<T>`] to be `Sync`
|
||||
/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`).
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
|
||||
|
||||
/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a
|
||||
/// subfield of the protected data. When this structure is dropped (falls out
|
||||
/// of scope), the lock will be unlocked.
|
||||
///
|
||||
/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the
|
||||
/// former cannot be used with [`Condvar`], since that could introduce soundness issues if the
|
||||
/// locked object is modified by another thread while the `Mutex` is unlocked.
|
||||
///
|
||||
/// The data protected by the mutex can be accessed through this guard via its
|
||||
/// [`Deref`] and [`DerefMut`] implementations.
|
||||
///
|
||||
/// This structure is created by the [`map`] and [`filter_map`] methods on
|
||||
/// [`MutexGuard`].
|
||||
///
|
||||
/// [`map`]: MutexGuard::map
|
||||
/// [`filter_map`]: MutexGuard::filter_map
|
||||
/// [`Condvar`]: crate::sync::nonpoison::Condvar
|
||||
#[must_use = "if unused the Mutex will immediately unlock"]
|
||||
#[must_not_suspend = "holding a MappedMutexGuard across suspend \
|
||||
points can cause deadlocks, delays, \
|
||||
and cause Futures to not implement `Send`"]
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[clippy::has_significant_drop]
|
||||
pub struct MappedMutexGuard<'a, T: ?Sized + 'a> {
|
||||
// NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a
|
||||
// `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops.
|
||||
// `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field
|
||||
// below for the correct variance over `T` (invariance).
|
||||
data: NonNull<T>,
|
||||
inner: &'a sys::Mutex,
|
||||
_variance: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> !Send for MappedMutexGuard<'_, T> {}
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
unsafe impl<T: ?Sized + Sync> Sync for MappedMutexGuard<'_, T> {}
|
||||
|
||||
impl<T> Mutex<T> {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(0);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
#[inline]
|
||||
pub const fn new(t: T) -> Mutex<T> {
|
||||
Mutex { inner: sys::Mutex::new(), data: UnsafeCell::new(t) }
|
||||
}
|
||||
|
||||
/// Returns the contained value by cloning it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned(), 7);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn get_cloned(&self) -> T
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.lock().clone()
|
||||
}
|
||||
|
||||
/// Sets the contained value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned(), 7);
|
||||
/// mutex.set(11);
|
||||
/// assert_eq!(mutex.get_cloned(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn set(&self, value: T) {
|
||||
if mem::needs_drop::<T>() {
|
||||
// If the contained value has a non-trivial destructor, we
|
||||
// call that destructor after the lock has been released.
|
||||
drop(self.replace(value))
|
||||
} else {
|
||||
*self.lock() = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contained value with `value`, and returns the old contained value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.replace(11), 7);
|
||||
/// assert_eq!(mutex.get_cloned(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn replace(&self, value: T) -> T {
|
||||
let mut guard = self.lock();
|
||||
mem::replace(&mut *guard, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Mutex<T> {
|
||||
/// Acquires a mutex, blocking the current thread until it is able to do so.
|
||||
///
|
||||
/// This function will block the local thread until it is available to acquire
|
||||
/// the mutex. Upon returning, the thread is the only thread with the lock
|
||||
/// held. An RAII guard is returned to allow scoped unlock of the lock. When
|
||||
/// the guard goes out of scope, the mutex will be unlocked.
|
||||
///
|
||||
/// The exact behavior on locking a mutex in the thread which already holds
|
||||
/// the lock is left unspecified. However, this function will not return on
|
||||
/// the second call (it might panic or deadlock, for example).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function might panic when called if the lock is already held by
|
||||
/// the current thread.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::{Arc, nonpoison::Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// *c_mutex.lock() = 10;
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(*mutex.lock(), 10);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn lock(&self) -> MutexGuard<'_, T> {
|
||||
unsafe {
|
||||
self.inner.lock();
|
||||
MutexGuard::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to acquire this lock.
|
||||
///
|
||||
/// This function does not block. If the lock could not be acquired at this time, then
|
||||
/// [`WouldBlock`] is returned. Otherwise, an RAII guard is returned.
|
||||
///
|
||||
/// The lock will be unlocked when the guard is dropped.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the mutex could not be acquired because it is already locked, then this call will return
|
||||
/// the [`WouldBlock`] error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(0));
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let mut lock = c_mutex.try_lock();
|
||||
/// if let Ok(ref mut mutex) = lock {
|
||||
/// **mutex = 10;
|
||||
/// } else {
|
||||
/// println!("try_lock failed");
|
||||
/// }
|
||||
/// }).join().expect("thread::spawn failed");
|
||||
/// assert_eq!(*mutex.lock().unwrap(), 10);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
|
||||
unsafe { if self.inner.try_lock() { Ok(MutexGuard::new(self)) } else { Err(WouldBlock) } }
|
||||
}
|
||||
|
||||
/// Consumes this mutex, returning the underlying data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(0);
|
||||
/// assert_eq!(mutex.into_inner(), 0);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn into_inner(self) -> T
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
self.data.into_inner()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying data.
|
||||
///
|
||||
/// Since this call borrows the `Mutex` mutably, no actual locking needs to
|
||||
/// take place -- the mutable borrow statically guarantees no locks exist.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(0);
|
||||
/// *mutex.get_mut() = 10;
|
||||
/// assert_eq!(*mutex.lock(), 10);
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.data.get_mut()
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the underlying data.
|
||||
///
|
||||
/// The returned pointer is always non-null and properly aligned, but it is
|
||||
/// the user's responsibility to ensure that any reads and writes through it
|
||||
/// are properly synchronized to avoid data races, and that it is not read
|
||||
/// or written through after the mutex is dropped.
|
||||
#[unstable(feature = "mutex_data_ptr", issue = "140368")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub const fn data_ptr(&self) -> *mut T {
|
||||
self.data.get()
|
||||
}
|
||||
|
||||
/// Acquires the mutex and provides mutable access to the underlying data by passing
|
||||
/// a mutable reference to the given closure.
|
||||
///
|
||||
/// This method acquires the lock, calls the provided closure with a mutable reference
|
||||
/// to the data, and returns the result of the closure. The lock is released after
|
||||
/// the closure completes, even if it panics.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors, nonpoison_mutex)]
|
||||
///
|
||||
/// use std::sync::nonpoison::Mutex;
|
||||
///
|
||||
/// let mutex = Mutex::new(2);
|
||||
///
|
||||
/// let result = mutex.with_mut(|data| {
|
||||
/// *data += 3;
|
||||
///
|
||||
/// *data + 5
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(*mutex.lock(), 5);
|
||||
/// assert_eq!(result, 10);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn with_mut<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut T) -> R,
|
||||
{
|
||||
f(&mut self.lock())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T> From<T> for Mutex<T> {
|
||||
/// Creates a new mutex in an unlocked state ready for use.
|
||||
/// This is equivalent to [`Mutex::new`].
|
||||
fn from(t: T) -> Self {
|
||||
Mutex::new(t)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: Default> Default for Mutex<T> {
|
||||
/// Creates a `Mutex<T>`, with the `Default` value for T.
|
||||
fn default() -> Mutex<T> {
|
||||
Mutex::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("Mutex");
|
||||
match self.try_lock() {
|
||||
Ok(guard) => {
|
||||
d.field("data", &&*guard);
|
||||
}
|
||||
Err(WouldBlock) => {
|
||||
d.field("data", &"<locked>");
|
||||
}
|
||||
}
|
||||
d.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
|
||||
unsafe fn new(lock: &'mutex Mutex<T>) -> MutexGuard<'mutex, T> {
|
||||
return MutexGuard { lock };
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.lock.data.get() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.lock.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// For use in [`nonpoison::condvar`](super::condvar).
|
||||
pub(super) fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex {
|
||||
&guard.lock.inner
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> MutexGuard<'a, T> {
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
|
||||
/// an enum variant.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MutexGuard::map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData }
|
||||
}
|
||||
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
|
||||
/// original guard is returned as an `Err(...)` if the closure returns
|
||||
/// `None`.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MutexGuard::filter_map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
match f(unsafe { &mut *orig.lock.data.get() }) {
|
||||
Some(data) => {
|
||||
let data = NonNull::from(data);
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
Ok(MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData })
|
||||
}
|
||||
None => Err(orig),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> Deref for MappedMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { self.data.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> DerefMut for MappedMutexGuard<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { self.data.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized> Drop for MappedMutexGuard<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.inner.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for MappedMutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for MappedMutexGuard<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> MappedMutexGuard<'a, T> {
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g.
|
||||
/// an enum variant.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MappedMutexGuard::map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U>
|
||||
where
|
||||
F: FnOnce(&mut T) -> &mut U,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData }
|
||||
}
|
||||
|
||||
/// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The
|
||||
/// original guard is returned as an `Err(...)` if the closure returns
|
||||
/// `None`.
|
||||
///
|
||||
/// The `Mutex` is already locked, so this cannot fail.
|
||||
///
|
||||
/// This is an associated function that needs to be used as
|
||||
/// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the
|
||||
/// same name on the contents of the `MutexGuard` used through `Deref`.
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
// #[unstable(feature = "nonpoison_mutex", issue = "134645")]
|
||||
pub fn filter_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U>, Self>
|
||||
where
|
||||
F: FnOnce(&mut T) -> Option<&mut U>,
|
||||
U: ?Sized,
|
||||
{
|
||||
// SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard
|
||||
// was created, and have been upheld throughout `map` and/or `filter_map`.
|
||||
// The signature of the closure guarantees that it will not "leak" the lifetime of the reference
|
||||
// passed to it. If the closure panics, the guard will be dropped.
|
||||
match f(unsafe { orig.data.as_mut() }) {
|
||||
Some(data) => {
|
||||
let data = NonNull::from(data);
|
||||
let orig = ManuallyDrop::new(orig);
|
||||
Ok(MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData })
|
||||
}
|
||||
None => Err(orig),
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,395 +0,0 @@
|
||||
//! A "once initialization" primitive
|
||||
//!
|
||||
//! This primitive is meant to be used to run one-time initialization. An
|
||||
//! example use case would be for initializing an FFI library.
|
||||
|
||||
use crate::fmt;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A low-level synchronization primitive for one-time global execution.
|
||||
///
|
||||
/// Previously this was the only "execute once" synchronization in `std`.
|
||||
/// Other libraries implemented novel synchronizing types with `Once`, like
|
||||
/// [`OnceLock<T>`] or [`LazyLock<T, F>`], before those were added to `std`.
|
||||
/// `OnceLock<T>` in particular supersedes `Once` in functionality and should
|
||||
/// be preferred for the common case where the `Once` is associated with data.
|
||||
///
|
||||
/// This type can only be constructed with [`Once::new()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static START: Once = Once::new();
|
||||
///
|
||||
/// START.call_once(|| {
|
||||
/// // run initialization here
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`OnceLock<T>`]: crate::sync::OnceLock
|
||||
/// [`LazyLock<T, F>`]: crate::sync::LazyLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Once {
|
||||
inner: sys::Once,
|
||||
}
|
||||
|
||||
#[stable(feature = "sync_once_unwind_safe", since = "1.59.0")]
|
||||
impl UnwindSafe for Once {}
|
||||
|
||||
#[stable(feature = "sync_once_unwind_safe", since = "1.59.0")]
|
||||
impl RefUnwindSafe for Once {}
|
||||
|
||||
/// State yielded to [`Once::call_once_force()`]’s closure parameter. The state
|
||||
/// can be used to query the poison status of the [`Once`].
|
||||
#[stable(feature = "once_poison", since = "1.51.0")]
|
||||
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,
|
||||
}
|
||||
|
||||
/// Initialization value for static [`Once`] values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Once, ONCE_INIT};
|
||||
///
|
||||
/// static START: Once = ONCE_INIT;
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[deprecated(
|
||||
since = "1.38.0",
|
||||
note = "the `Once::new()` function is now preferred",
|
||||
suggestion = "Once::new()"
|
||||
)]
|
||||
pub const ONCE_INIT: Once = Once::new();
|
||||
|
||||
impl Once {
|
||||
/// Creates a new `Once` value.
|
||||
#[inline]
|
||||
#[stable(feature = "once_new", since = "1.2.0")]
|
||||
#[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
|
||||
#[must_use]
|
||||
pub const fn new() -> Once {
|
||||
Once { inner: sys::Once::new() }
|
||||
}
|
||||
|
||||
/// Performs an initialization routine once and only once. The given closure
|
||||
/// will be executed if this is the first time `call_once` has been called,
|
||||
/// and otherwise the routine will *not* be invoked.
|
||||
///
|
||||
/// This method will block the calling thread if another initialization
|
||||
/// routine is currently running.
|
||||
///
|
||||
/// When this function returns, it is guaranteed that some initialization
|
||||
/// has run and completed (it might not be the closure specified). It is also
|
||||
/// guaranteed that any memory writes performed by the executed closure can
|
||||
/// be reliably observed by other threads at this point (there is a
|
||||
/// happens-before relation between the closure and code executing after the
|
||||
/// return).
|
||||
///
|
||||
/// If the given closure recursively invokes `call_once` on the same [`Once`]
|
||||
/// instance, the exact behavior is not specified: allowed outcomes are
|
||||
/// a panic or a deadlock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static mut VAL: usize = 0;
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// // Accessing a `static mut` is unsafe much of the time, but if we do so
|
||||
/// // in a synchronized fashion (e.g., write once or read all) then we're
|
||||
/// // good to go!
|
||||
/// //
|
||||
/// // This function will only call `expensive_computation` once, and will
|
||||
/// // otherwise always return the value returned from the first invocation.
|
||||
/// fn get_cached_val() -> usize {
|
||||
/// unsafe {
|
||||
/// INIT.call_once(|| {
|
||||
/// VAL = expensive_computation();
|
||||
/// });
|
||||
/// VAL
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn expensive_computation() -> usize {
|
||||
/// // ...
|
||||
/// # 2
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The closure `f` will only be executed once even if this is called
|
||||
/// concurrently amongst many threads. If that closure panics, however, then
|
||||
/// it will *poison* this [`Once`] instance, causing all future invocations of
|
||||
/// `call_once` to also panic.
|
||||
///
|
||||
/// This is similar to [poisoning with mutexes][poison], but this mechanism
|
||||
/// is guaranteed to never skip panics within `f`.
|
||||
///
|
||||
/// [poison]: struct.Mutex.html#poisoning
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[track_caller]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn call_once<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
// Fast path check
|
||||
if self.inner.is_completed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
self.inner.call(false, &mut |_| f.take().unwrap()());
|
||||
}
|
||||
|
||||
/// Performs the same function as [`call_once()`] except ignores poisoning.
|
||||
///
|
||||
/// Unlike [`call_once()`], if this [`Once`] has been poisoned (i.e., a previous
|
||||
/// call to [`call_once()`] or [`call_once_force()`] caused a panic), calling
|
||||
/// [`call_once_force()`] will still invoke the closure `f` and will _not_
|
||||
/// result in an immediate panic. If `f` panics, the [`Once`] will remain
|
||||
/// in a poison state. If `f` does _not_ panic, the [`Once`] will no
|
||||
/// longer be in a poison state and all future calls to [`call_once()`] or
|
||||
/// [`call_once_force()`] will be no-ops.
|
||||
///
|
||||
/// The closure `f` is yielded a [`OnceState`] structure which can be used
|
||||
/// to query the poison status of the [`Once`].
|
||||
///
|
||||
/// [`call_once()`]: Once::call_once
|
||||
/// [`call_once_force()`]: Once::call_once_force
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// // poison the once
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| panic!());
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
///
|
||||
/// // poisoning propagates
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| {});
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
///
|
||||
/// // call_once_force will still run and reset the poisoned state
|
||||
/// INIT.call_once_force(|state| {
|
||||
/// assert!(state.is_poisoned());
|
||||
/// });
|
||||
///
|
||||
/// // once any success happens, we stop propagating the poison
|
||||
/// INIT.call_once(|| {});
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_poison", since = "1.51.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn call_once_force<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce(&OnceState),
|
||||
{
|
||||
// Fast path check
|
||||
if self.inner.is_completed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut f = Some(f);
|
||||
self.inner.call(true, &mut |p| f.take().unwrap()(p));
|
||||
}
|
||||
|
||||
/// Returns `true` if some [`call_once()`] call has completed
|
||||
/// successfully. Specifically, `is_completed` will return false in
|
||||
/// the following situations:
|
||||
/// * [`call_once()`] was not called at all,
|
||||
/// * [`call_once()`] was called, but has not yet completed,
|
||||
/// * the [`Once`] instance is poisoned
|
||||
///
|
||||
/// This function returning `false` does not mean that [`Once`] has not been
|
||||
/// executed. For example, it may have been executed in the time between
|
||||
/// when `is_completed` starts executing and when it returns, in which case
|
||||
/// the `false` return value would be stale (but still permissible).
|
||||
///
|
||||
/// [`call_once()`]: Once::call_once
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// INIT.call_once(|| {
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// });
|
||||
/// assert_eq!(INIT.is_completed(), true);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| panic!());
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
/// assert_eq!(INIT.is_completed(), false);
|
||||
/// ```
|
||||
#[stable(feature = "once_is_completed", since = "1.43.0")]
|
||||
#[inline]
|
||||
pub fn is_completed(&self) -> bool {
|
||||
self.inner.is_completed()
|
||||
}
|
||||
|
||||
/// Blocks the current thread until initialization has completed.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static READY: Once = Once::new();
|
||||
///
|
||||
/// let thread = thread::spawn(|| {
|
||||
/// READY.wait();
|
||||
/// println!("everything is ready");
|
||||
/// });
|
||||
///
|
||||
/// READY.call_once(|| println!("performing setup"));
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If this [`Once`] has been poisoned because an initialization closure has
|
||||
/// panicked, this method will also panic. Use [`wait_force`](Self::wait_force)
|
||||
/// if this behavior is not desired.
|
||||
#[stable(feature = "once_wait", since = "1.86.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait(&self) {
|
||||
if !self.inner.is_completed() {
|
||||
self.inner.wait(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until initialization has completed, ignoring
|
||||
/// poisoning.
|
||||
///
|
||||
/// If this [`Once`] has been poisoned, this function blocks until it
|
||||
/// becomes completed, unlike [`Once::wait()`], which panics in this case.
|
||||
#[stable(feature = "once_wait", since = "1.86.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_force(&self) {
|
||||
if !self.inner.is_completed() {
|
||||
self.inner.wait(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current state of the `Once` instance.
|
||||
///
|
||||
/// Since this takes a mutable reference, no initialization can currently
|
||||
/// be running, so the state must be either "incomplete", "poisoned" or
|
||||
/// "complete".
|
||||
#[inline]
|
||||
pub(crate) fn state(&mut self) -> OnceExclusiveState {
|
||||
self.inner.state()
|
||||
}
|
||||
|
||||
/// Sets current state of the `Once` instance.
|
||||
///
|
||||
/// Since this takes a mutable reference, no initialization can currently
|
||||
/// be running, so the state must be either "incomplete", "poisoned" or
|
||||
/// "complete".
|
||||
#[inline]
|
||||
pub(crate) fn set_state(&mut self, new_state: OnceExclusiveState) {
|
||||
self.inner.set_state(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Once {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Once").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl OnceState {
|
||||
/// Returns `true` if the associated [`Once`] was poisoned prior to the
|
||||
/// invocation of the closure passed to [`Once::call_once_force()`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A poisoned [`Once`]:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// // poison the once
|
||||
/// let handle = thread::spawn(|| {
|
||||
/// INIT.call_once(|| panic!());
|
||||
/// });
|
||||
/// assert!(handle.join().is_err());
|
||||
///
|
||||
/// INIT.call_once_force(|state| {
|
||||
/// assert!(state.is_poisoned());
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// An unpoisoned [`Once`]:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Once;
|
||||
///
|
||||
/// static INIT: Once = Once::new();
|
||||
///
|
||||
/// INIT.call_once_force(|state| {
|
||||
/// assert!(!state.is_poisoned());
|
||||
/// });
|
||||
#[stable(feature = "once_poison", since = "1.51.0")]
|
||||
#[inline]
|
||||
pub fn is_poisoned(&self) -> bool {
|
||||
self.inner.is_poisoned()
|
||||
}
|
||||
|
||||
/// Poison the associated [`Once`] without explicitly panicking.
|
||||
// NOTE: This is currently only exposed for `OnceLock`.
|
||||
#[inline]
|
||||
pub(crate) fn poison(&self) {
|
||||
self.inner.poison();
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for OnceState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OnceState").field("poisoned", &self.is_poisoned()).finish()
|
||||
}
|
||||
}
|
||||
@@ -1,709 +0,0 @@
|
||||
use super::once::OnceExclusiveState;
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::MaybeUninit;
|
||||
use core::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::Once;
|
||||
|
||||
/// A synchronization primitive which can nominally be written to only once.
|
||||
///
|
||||
/// This type is a thread-safe [`OnceCell`], and can be used in statics.
|
||||
/// In many simple cases, you can use [`LazyLock<T, F>`] instead to get the benefits of this type
|
||||
/// with less effort: `LazyLock<T, F>` "looks like" `&T` because it initializes with `F` on deref!
|
||||
/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock
|
||||
/// doesn't allow additional inputs to its function after you call [`LazyLock::new(|| ...)`].
|
||||
///
|
||||
/// A `OnceLock` can be thought of as a safe abstraction over uninitialized data that becomes
|
||||
/// initialized once written.
|
||||
///
|
||||
/// Unlike [`Mutex`](crate::sync::Mutex), `OnceLock` is never poisoned on panic.
|
||||
///
|
||||
/// [`OnceCell`]: crate::cell::OnceCell
|
||||
/// [`LazyLock<T, F>`]: crate::sync::LazyLock
|
||||
/// [`LazyLock::new(|| ...)`]: crate::sync::LazyLock::new
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Writing to a `OnceLock` from a separate thread:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<usize> = OnceLock::new();
|
||||
///
|
||||
/// // `OnceLock` has not been written to yet.
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// // Spawn a thread and write to `OnceLock`.
|
||||
/// std::thread::spawn(|| {
|
||||
/// let value = CELL.get_or_init(|| 12345);
|
||||
/// assert_eq!(value, &12345);
|
||||
/// })
|
||||
/// .join()
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // `OnceLock` now contains the value.
|
||||
/// assert_eq!(
|
||||
/// CELL.get(),
|
||||
/// Some(&12345),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// You can use `OnceLock` to implement a type that requires "append-only" logic:
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// struct OnceList<T> {
|
||||
/// data: OnceLock<T>,
|
||||
/// next: OnceLock<Box<OnceList<T>>>,
|
||||
/// }
|
||||
/// impl<T> OnceList<T> {
|
||||
/// const fn new() -> OnceList<T> {
|
||||
/// OnceList { data: OnceLock::new(), next: OnceLock::new() }
|
||||
/// }
|
||||
/// fn push(&self, value: T) {
|
||||
/// // FIXME: this impl is concise, but is also slow for long lists or many threads.
|
||||
/// // as an exercise, consider how you might improve on it while preserving the behavior
|
||||
/// if let Err(value) = self.data.set(value) {
|
||||
/// let next = self.next.get_or_init(|| Box::new(OnceList::new()));
|
||||
/// next.push(value)
|
||||
/// };
|
||||
/// }
|
||||
/// fn contains(&self, example: &T) -> bool
|
||||
/// where
|
||||
/// T: PartialEq,
|
||||
/// {
|
||||
/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| {
|
||||
/// self.next.get().map(|next| next.contains(example)).unwrap_or(false)
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // Let's exercise this new Sync append-only list by doing a little counting
|
||||
/// static LIST: OnceList<u32> = OnceList::new();
|
||||
/// static COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||
///
|
||||
/// # const LEN: u32 = if cfg!(miri) { 50 } else { 1000 };
|
||||
/// # /*
|
||||
/// const LEN: u32 = 1000;
|
||||
/// # */
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..thread::available_parallelism().unwrap().get() {
|
||||
/// s.spawn(|| {
|
||||
/// while let i @ 0..LEN = COUNTER.fetch_add(1, Ordering::Relaxed) {
|
||||
/// LIST.push(i);
|
||||
/// }
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// for i in 0..LEN {
|
||||
/// assert!(LIST.contains(&i));
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub struct OnceLock<T> {
|
||||
// FIXME(nonpoison_once): switch to nonpoison version once it is available
|
||||
once: Once,
|
||||
// Whether or not the value is initialized is tracked by `once.is_completed()`.
|
||||
value: UnsafeCell<MaybeUninit<T>>,
|
||||
/// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl.
|
||||
///
|
||||
/// ```compile_fail,E0597
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// struct A<'a>(&'a str);
|
||||
///
|
||||
/// impl<'a> Drop for A<'a> {
|
||||
/// fn drop(&mut self) {}
|
||||
/// }
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// {
|
||||
/// let s = String::new();
|
||||
/// let _ = cell.set(A(&s));
|
||||
/// }
|
||||
/// ```
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> OnceLock<T> {
|
||||
/// Creates a new uninitialized cell.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_const_stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub const fn new() -> OnceLock<T> {
|
||||
OnceLock {
|
||||
once: Once::new(),
|
||||
value: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is uninitialized, or being initialized.
|
||||
/// This method never blocks.
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
if self.initialized() {
|
||||
// Safe b/c checked initialized
|
||||
Some(unsafe { self.get_unchecked() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the mutable reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is uninitialized.
|
||||
///
|
||||
/// This method never blocks. Since it borrows the `OnceLock` mutably,
|
||||
/// it is statically guaranteed that no active borrows to the `OnceLock`
|
||||
/// exist, including from other threads.
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||
if self.initialized_mut() {
|
||||
// Safe b/c checked initialized and we have a unique access
|
||||
Some(unsafe { self.get_unchecked_mut() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocks the current thread until the cell is initialized.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Waiting for a computation on another thread to finish:
|
||||
/// ```rust
|
||||
/// use std::thread;
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let value = OnceLock::new();
|
||||
///
|
||||
/// thread::scope(|s| {
|
||||
/// s.spawn(|| value.set(1 + 1));
|
||||
///
|
||||
/// let result = value.wait();
|
||||
/// assert_eq!(result, &2);
|
||||
/// })
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_wait", since = "1.86.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait(&self) -> &T {
|
||||
self.once.wait_force();
|
||||
|
||||
unsafe { self.get_unchecked() }
|
||||
}
|
||||
|
||||
/// Initializes the contents of the cell to `value`.
|
||||
///
|
||||
/// May block if another thread is currently attempting to initialize the cell. The cell is
|
||||
/// guaranteed to contain a value when `set` returns, though not necessarily the one provided.
|
||||
///
|
||||
/// Returns `Ok(())` if the cell was uninitialized and
|
||||
/// `Err(value)` if the cell was already initialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<i32> = OnceLock::new();
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// std::thread::spawn(|| {
|
||||
/// assert_eq!(CELL.set(92), Ok(()));
|
||||
/// }).join().unwrap();
|
||||
///
|
||||
/// assert_eq!(CELL.set(62), Err(62));
|
||||
/// assert_eq!(CELL.get(), Some(&92));
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn set(&self, value: T) -> Result<(), T> {
|
||||
match self.try_insert(value) {
|
||||
Ok(_) => Ok(()),
|
||||
Err((_, value)) => Err(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the contents of the cell to `value` if the cell was uninitialized,
|
||||
/// then returns a reference to it.
|
||||
///
|
||||
/// May block if another thread is currently attempting to initialize the cell. The cell is
|
||||
/// guaranteed to contain a value when `try_insert` returns, though not necessarily the
|
||||
/// one provided.
|
||||
///
|
||||
/// Returns `Ok(&value)` if the cell was uninitialized and
|
||||
/// `Err((¤t_value, value))` if it was already initialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_try_insert)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<i32> = OnceLock::new();
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// std::thread::spawn(|| {
|
||||
/// assert_eq!(CELL.try_insert(92), Ok(&92));
|
||||
/// }).join().unwrap();
|
||||
///
|
||||
/// assert_eq!(CELL.try_insert(62), Err((&92, 62)));
|
||||
/// assert_eq!(CELL.get(), Some(&92));
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_try_insert", issue = "116693")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
|
||||
let mut value = Some(value);
|
||||
let res = self.get_or_init(|| value.take().unwrap());
|
||||
match value {
|
||||
None => Ok(res),
|
||||
Some(value) => Err((res, value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the contents of the cell, initializing it to `f()` if the cell
|
||||
/// was uninitialized.
|
||||
///
|
||||
/// Many threads may call `get_or_init` concurrently with different
|
||||
/// initializing functions, but it is guaranteed that only one function
|
||||
/// will be executed if the function doesn't panic.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and the cell
|
||||
/// remains uninitialized.
|
||||
///
|
||||
/// It is an error to reentrantly initialize the cell from `f`. The
|
||||
/// exact outcome is unspecified. Current implementation deadlocks, but
|
||||
/// this may be changed to a panic in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// let value = cell.get_or_init(|| 92);
|
||||
/// assert_eq!(value, &92);
|
||||
/// let value = cell.get_or_init(|| unreachable!());
|
||||
/// assert_eq!(value, &92);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get_or_init<F>(&self, f: F) -> &T
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
match self.get_or_try_init(|| Ok::<T, !>(f())) {
|
||||
Ok(val) => val,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the mutable reference of the contents of the cell, initializing
|
||||
/// it to `f()` if the cell was uninitialized.
|
||||
///
|
||||
/// This method never blocks. Since it borrows the `OnceLock` mutably,
|
||||
/// it is statically guaranteed that no active borrows to the `OnceLock`
|
||||
/// exist, including from other threads.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and the cell
|
||||
/// remains uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_get_mut)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let mut cell = OnceLock::new();
|
||||
/// let value = cell.get_mut_or_init(|| 92);
|
||||
/// assert_eq!(*value, 92);
|
||||
///
|
||||
/// *value += 2;
|
||||
/// assert_eq!(*value, 94);
|
||||
///
|
||||
/// let value = cell.get_mut_or_init(|| unreachable!());
|
||||
/// assert_eq!(*value, 94);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
|
||||
pub fn get_mut_or_init<F>(&mut self, f: F) -> &mut T
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
match self.get_mut_or_try_init(|| Ok::<T, !>(f())) {
|
||||
Ok(val) => val,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the contents of the cell, initializing it to `f()` if
|
||||
/// the cell was uninitialized. If the cell was uninitialized
|
||||
/// and `f()` failed, an error is returned.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and
|
||||
/// the cell remains uninitialized.
|
||||
///
|
||||
/// It is an error to reentrantly initialize the cell from `f`.
|
||||
/// The exact outcome is unspecified. Current implementation
|
||||
/// deadlocks, but this may be changed to a panic in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_try)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
|
||||
/// assert!(cell.get().is_none());
|
||||
/// let value = cell.get_or_try_init(|| -> Result<i32, ()> {
|
||||
/// Ok(92)
|
||||
/// });
|
||||
/// assert_eq!(value, Ok(&92));
|
||||
/// assert_eq!(cell.get(), Some(&92))
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_try", issue = "109737")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
// Fast path check
|
||||
// NOTE: We need to perform an acquire on the state in this method
|
||||
// in order to correctly synchronize `LazyLock::force`. This is
|
||||
// currently done by calling `self.get()`, which in turn calls
|
||||
// `self.initialized()`, which in turn performs the acquire.
|
||||
if let Some(value) = self.get() {
|
||||
return Ok(value);
|
||||
}
|
||||
self.initialize(f)?;
|
||||
|
||||
// SAFETY: The inner value has been initialized
|
||||
Ok(unsafe { self.get_unchecked() })
|
||||
}
|
||||
|
||||
/// Gets the mutable reference of the contents of the cell, initializing
|
||||
/// it to `f()` if the cell was uninitialized. If the cell was uninitialized
|
||||
/// and `f()` failed, an error is returned.
|
||||
///
|
||||
/// This method never blocks. Since it borrows the `OnceLock` mutably,
|
||||
/// it is statically guaranteed that no active borrows to the `OnceLock`
|
||||
/// exist, including from other threads.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f()` panics, the panic is propagated to the caller, and
|
||||
/// the cell remains uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell_get_mut)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let mut cell: OnceLock<u32> = OnceLock::new();
|
||||
///
|
||||
/// // Failed attempts to initialize the cell do not change its contents
|
||||
/// assert!(cell.get_mut_or_try_init(|| "not a number!".parse()).is_err());
|
||||
/// assert!(cell.get().is_none());
|
||||
///
|
||||
/// let value = cell.get_mut_or_try_init(|| "1234".parse());
|
||||
/// assert_eq!(value, Ok(&mut 1234));
|
||||
/// *value.unwrap() += 2;
|
||||
/// assert_eq!(cell.get(), Some(&1236))
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "once_cell_get_mut", issue = "121641")]
|
||||
pub fn get_mut_or_try_init<F, E>(&mut self, f: F) -> Result<&mut T, E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
if self.get_mut().is_none() {
|
||||
self.initialize(f)?;
|
||||
}
|
||||
|
||||
// SAFETY: The inner value has been initialized
|
||||
Ok(unsafe { self.get_unchecked_mut() })
|
||||
}
|
||||
|
||||
/// Consumes the `OnceLock`, returning the wrapped value. Returns
|
||||
/// `None` if the cell was uninitialized.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell: OnceLock<String> = OnceLock::new();
|
||||
/// assert_eq!(cell.into_inner(), None);
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub fn into_inner(mut self) -> Option<T> {
|
||||
self.take()
|
||||
}
|
||||
|
||||
/// Takes the value out of this `OnceLock`, moving it back to an uninitialized state.
|
||||
///
|
||||
/// Has no effect and returns `None` if the `OnceLock` was uninitialized.
|
||||
///
|
||||
/// Since this method borrows the `OnceLock` mutably, it is statically guaranteed that
|
||||
/// no active borrows to the `OnceLock` exist, including from other threads.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let mut cell: OnceLock<String> = OnceLock::new();
|
||||
/// assert_eq!(cell.take(), None);
|
||||
///
|
||||
/// let mut cell = OnceLock::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.take(), Some("hello".to_string()));
|
||||
/// assert_eq!(cell.get(), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
pub fn take(&mut self) -> Option<T> {
|
||||
if self.initialized_mut() {
|
||||
self.once = Once::new();
|
||||
// SAFETY: `self.value` is initialized and contains a valid `T`.
|
||||
// `self.once` is reset, so `initialized()` will be false again
|
||||
// which prevents the value from being read twice.
|
||||
unsafe { Some(self.value.get_mut().assume_init_read()) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initialized(&self) -> bool {
|
||||
self.once.is_completed()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initialized_mut(&mut self) -> bool {
|
||||
// `state()` does not perform an atomic load, so prefer it over `is_complete()`.
|
||||
let state = self.once.state();
|
||||
match state {
|
||||
OnceExclusiveState::Complete => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
#[optimize(size)]
|
||||
fn initialize<F, E>(&self, f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
let mut res: Result<(), E> = Ok(());
|
||||
let slot = &self.value;
|
||||
|
||||
// Ignore poisoning from other threads
|
||||
// If another thread panics, then we'll be able to run our closure
|
||||
self.once.call_once_force(|p| {
|
||||
match f() {
|
||||
Ok(value) => {
|
||||
unsafe { (&mut *slot.get()).write(value) };
|
||||
}
|
||||
Err(e) => {
|
||||
res = Err(e);
|
||||
|
||||
// Treat the underlying `Once` as poisoned since we
|
||||
// failed to initialize our value.
|
||||
p.poison();
|
||||
}
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The cell must be initialized
|
||||
#[inline]
|
||||
unsafe fn get_unchecked(&self) -> &T {
|
||||
debug_assert!(self.initialized());
|
||||
unsafe { (&*self.value.get()).assume_init_ref() }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The cell must be initialized
|
||||
#[inline]
|
||||
unsafe fn get_unchecked_mut(&mut self) -> &mut T {
|
||||
debug_assert!(self.initialized_mut());
|
||||
unsafe { self.value.get_mut().assume_init_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
// Why do we need `T: Send`?
|
||||
// Thread A creates a `OnceLock` and shares it with
|
||||
// scoped thread B, which fills the cell, which is
|
||||
// then destroyed by A. That is, destructor observes
|
||||
// a sent value.
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
unsafe impl<T: Send> Send for OnceLock<T> {}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
#[rustc_const_unstable(feature = "const_default", issue = "143894")]
|
||||
impl<T> const Default for OnceLock<T> {
|
||||
/// Creates a new uninitialized cell.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert_eq!(OnceLock::<()>::new(), OnceLock::default());
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
fn default() -> OnceLock<T> {
|
||||
OnceLock::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_tuple("OnceLock");
|
||||
match self.get() {
|
||||
Some(v) => d.field(v),
|
||||
None => d.field(&format_args!("<uninit>")),
|
||||
};
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: Clone> Clone for OnceLock<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> OnceLock<T> {
|
||||
let cell = Self::new();
|
||||
if let Some(value) = self.get() {
|
||||
match cell.set(value.clone()) {
|
||||
Ok(()) => (),
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
cell
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T> From<T> for OnceLock<T> {
|
||||
/// Creates a new cell with its contents set to `value`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// # fn main() -> Result<(), i32> {
|
||||
/// let a = OnceLock::from(3);
|
||||
/// let b = OnceLock::new();
|
||||
/// b.set(3)?;
|
||||
/// assert_eq!(a, b);
|
||||
/// Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
fn from(value: T) -> Self {
|
||||
let cell = Self::new();
|
||||
match cell.set(value) {
|
||||
Ok(()) => cell,
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: PartialEq> PartialEq for OnceLock<T> {
|
||||
/// Equality for two `OnceLock`s.
|
||||
///
|
||||
/// Two `OnceLock`s are equal if they either both contain values and their
|
||||
/// values are equal, or if neither contains a value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let five = OnceLock::new();
|
||||
/// five.set(5).unwrap();
|
||||
///
|
||||
/// let also_five = OnceLock::new();
|
||||
/// also_five.set(5).unwrap();
|
||||
///
|
||||
/// assert!(five == also_five);
|
||||
///
|
||||
/// assert!(OnceLock::<u32>::new() == OnceLock::<u32>::new());
|
||||
/// ```
|
||||
#[inline]
|
||||
fn eq(&self, other: &OnceLock<T>) -> bool {
|
||||
self.get() == other.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
impl<T: Eq> Eq for OnceLock<T> {}
|
||||
|
||||
#[stable(feature = "once_cell", since = "1.70.0")]
|
||||
unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.initialized_mut() {
|
||||
// SAFETY: The cell is initialized and being dropped, so it can't
|
||||
// be accessed again. We also don't touch the `T` other than
|
||||
// dropping it, which validates our usage of #[may_dangle].
|
||||
unsafe { self.value.get_mut().assume_init_drop() };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,389 +0,0 @@
|
||||
//! Synchronization objects that employ poisoning.
|
||||
//!
|
||||
//! # Poisoning
|
||||
//!
|
||||
//! All synchronization objects in this module implement a strategy called
|
||||
//! "poisoning" where a primitive becomes poisoned if it recognizes that some
|
||||
//! thread has panicked while holding the exclusive access granted by the
|
||||
//! primitive. This information is then propagated to all other threads
|
||||
//! to signify that the data protected by this primitive is likely tainted
|
||||
//! (some invariant is not being upheld).
|
||||
//!
|
||||
//! The specifics of how this "poisoned" state affects other threads and whether
|
||||
//! the panics are recognized reliably or on a best-effort basis depend on the
|
||||
//! primitive. See [Overview](#overview) below.
|
||||
//!
|
||||
//! The synchronization objects in this module have alternative implementations that do not employ
|
||||
//! poisoning in the [`std::sync::nonpoison`] module.
|
||||
//!
|
||||
//! [`std::sync::nonpoison`]: crate::sync::nonpoison
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Below is a list of synchronization objects provided by this module
|
||||
//! with a high-level overview for each object and a description
|
||||
//! of how it employs "poisoning".
|
||||
//!
|
||||
//! - [`Condvar`]: Condition Variable, providing the ability to block
|
||||
//! a thread while waiting for an event to occur.
|
||||
//!
|
||||
//! Condition variables are typically associated with
|
||||
//! a boolean predicate (a condition) and a mutex.
|
||||
//! This implementation is associated with [`poison::Mutex`](Mutex),
|
||||
//! which employs poisoning.
|
||||
//! For this reason, [`Condvar::wait()`] will return a [`LockResult`],
|
||||
//! just like [`poison::Mutex::lock()`](Mutex::lock) does.
|
||||
//!
|
||||
//! - [`Mutex`]: Mutual Exclusion mechanism, which ensures that at
|
||||
//! most one thread at a time is able to access some data.
|
||||
//!
|
||||
//! Panicking while holding the lock typically poisons the mutex, but it is
|
||||
//! not guaranteed to detect this condition in all circumstances.
|
||||
//! [`Mutex::lock()`] returns a [`LockResult`], providing a way to deal with
|
||||
//! the poisoned state. See [`Mutex`'s documentation](Mutex#poisoning) for more.
|
||||
//!
|
||||
//! - [`RwLock`]: Provides a mutual exclusion mechanism which allows
|
||||
//! multiple readers at the same time, while allowing only one
|
||||
//! writer at a time. In some cases, this can be more efficient than
|
||||
//! a mutex.
|
||||
//!
|
||||
//! This implementation, like [`Mutex`], usually becomes poisoned on a panic.
|
||||
//! Note, however, that an `RwLock` may only be poisoned if a panic occurs
|
||||
//! while it is locked exclusively (write mode). If a panic occurs in any reader,
|
||||
//! then the lock will not be poisoned.
|
||||
//!
|
||||
//! Note that the [`Once`] type also employs poisoning, but since it has non-poisoning `force`
|
||||
//! methods available on it, there is no separate `nonpoison` and `poison` version.
|
||||
//!
|
||||
//! [`Once`]: crate::sync::Once
|
||||
|
||||
// If we are not unwinding, `PoisonError` is uninhabited.
|
||||
#![cfg_attr(not(panic = "unwind"), expect(unreachable_code))]
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::condvar::Condvar;
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::mutex::MappedMutexGuard;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::mutex::{Mutex, MutexGuard};
|
||||
#[unstable(feature = "mapped_lock_guards", issue = "117108")]
|
||||
pub use self::rwlock::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use crate::error::Error;
|
||||
use crate::fmt;
|
||||
#[cfg(panic = "unwind")]
|
||||
use crate::sync::atomic::{Atomic, AtomicBool, Ordering};
|
||||
#[cfg(panic = "unwind")]
|
||||
use crate::thread;
|
||||
|
||||
mod condvar;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
mod mutex;
|
||||
mod rwlock;
|
||||
|
||||
pub(crate) struct Flag {
|
||||
#[cfg(panic = "unwind")]
|
||||
failed: Atomic<bool>,
|
||||
}
|
||||
|
||||
// Note that the Ordering uses to access the `failed` field of `Flag` below is
|
||||
// always `Relaxed`, and that's because this isn't actually protecting any data,
|
||||
// it's just a flag whether we've panicked or not.
|
||||
//
|
||||
// The actual location that this matters is when a mutex is **locked** which is
|
||||
// where we have external synchronization ensuring that we see memory
|
||||
// reads/writes to this flag.
|
||||
//
|
||||
// As a result, if it matters, we should see the correct value for `failed` in
|
||||
// all cases.
|
||||
|
||||
impl Flag {
|
||||
#[inline]
|
||||
pub const fn new() -> Flag {
|
||||
Flag {
|
||||
#[cfg(panic = "unwind")]
|
||||
failed: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the flag for an unguarded borrow, where we only care about existing poison.
|
||||
#[inline]
|
||||
pub fn borrow(&self) -> LockResult<()> {
|
||||
if self.get() { Err(PoisonError::new(())) } else { Ok(()) }
|
||||
}
|
||||
|
||||
/// Checks the flag for a guarded borrow, where we may also set poison when `done`.
|
||||
#[inline]
|
||||
pub fn guard(&self) -> LockResult<Guard> {
|
||||
let ret = Guard {
|
||||
#[cfg(panic = "unwind")]
|
||||
panicking: thread::panicking(),
|
||||
};
|
||||
if self.get() { Err(PoisonError::new(ret)) } else { Ok(ret) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(panic = "unwind")]
|
||||
pub fn done(&self, guard: &Guard) {
|
||||
if !guard.panicking && thread::panicking() {
|
||||
self.failed.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
pub fn done(&self, _guard: &Guard) {}
|
||||
|
||||
#[inline]
|
||||
#[cfg(panic = "unwind")]
|
||||
pub fn get(&self) -> bool {
|
||||
self.failed.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
pub fn get(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear(&self) {
|
||||
#[cfg(panic = "unwind")]
|
||||
self.failed.store(false, Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Guard {
|
||||
#[cfg(panic = "unwind")]
|
||||
panicking: bool,
|
||||
}
|
||||
|
||||
/// A type of error which can be returned whenever a lock is acquired.
|
||||
///
|
||||
/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
|
||||
/// is held. The precise semantics for when a lock is poisoned is documented on
|
||||
/// each lock. For a lock in the poisoned state, unless the state is cleared manually,
|
||||
/// all future acquisitions will return this error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(1));
|
||||
///
|
||||
/// // poison the mutex
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
/// let _ = thread::spawn(move || {
|
||||
/// let mut data = c_mutex.lock().unwrap();
|
||||
/// *data = 2;
|
||||
/// panic!();
|
||||
/// }).join();
|
||||
///
|
||||
/// match mutex.lock() {
|
||||
/// Ok(_) => unreachable!(),
|
||||
/// Err(p_err) => {
|
||||
/// let data = p_err.get_ref();
|
||||
/// println!("recovered: {data}");
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
/// [`Mutex`]: crate::sync::Mutex
|
||||
/// [`RwLock`]: crate::sync::RwLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct PoisonError<T> {
|
||||
data: T,
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
_never: !,
|
||||
}
|
||||
|
||||
/// An enumeration of possible errors associated with a [`TryLockResult`] which
|
||||
/// can occur while trying to acquire a lock, from the [`try_lock`] method on a
|
||||
/// [`Mutex`] or the [`try_read`] and [`try_write`] methods on an [`RwLock`].
|
||||
///
|
||||
/// [`try_lock`]: crate::sync::Mutex::try_lock
|
||||
/// [`try_read`]: crate::sync::RwLock::try_read
|
||||
/// [`try_write`]: crate::sync::RwLock::try_write
|
||||
/// [`Mutex`]: crate::sync::Mutex
|
||||
/// [`RwLock`]: crate::sync::RwLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub enum TryLockError<T> {
|
||||
/// The lock could not be acquired because another thread failed while holding
|
||||
/// the lock.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
Poisoned(#[stable(feature = "rust1", since = "1.0.0")] PoisonError<T>),
|
||||
/// The lock could not be acquired at this time because the operation would
|
||||
/// otherwise block.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
WouldBlock,
|
||||
}
|
||||
|
||||
/// A type alias for the result of a lock method which can be poisoned.
|
||||
///
|
||||
/// The [`Ok`] variant of this result indicates that the primitive was not
|
||||
/// poisoned, and the operation result is contained within. The [`Err`] variant indicates
|
||||
/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
|
||||
/// an associated value assigned by the lock method, and it can be acquired through the
|
||||
/// [`into_inner`] method. The semantics of the associated value depends on the corresponding
|
||||
/// lock method.
|
||||
///
|
||||
/// [`into_inner`]: PoisonError::into_inner
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub type LockResult<T> = Result<T, PoisonError<T>>;
|
||||
|
||||
/// A type alias for the result of a nonblocking locking method.
|
||||
///
|
||||
/// For more information, see [`LockResult`]. A `TryLockResult` doesn't
|
||||
/// necessarily hold the associated guard in the [`Err`] type as the lock might not
|
||||
/// have been acquired for other reasons.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Debug for PoisonError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PoisonError").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Display for PoisonError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
"poisoned lock: another task failed inside".fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Error for PoisonError<T> {}
|
||||
|
||||
impl<T> PoisonError<T> {
|
||||
/// Creates a `PoisonError`.
|
||||
///
|
||||
/// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
|
||||
/// or [`RwLock::read`](crate::sync::RwLock::read).
|
||||
///
|
||||
/// This method may panic if std was built with `panic="abort"`.
|
||||
#[cfg(panic = "unwind")]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn new(data: T) -> PoisonError<T> {
|
||||
PoisonError { data }
|
||||
}
|
||||
|
||||
/// Creates a `PoisonError`.
|
||||
///
|
||||
/// This is generally created by methods like [`Mutex::lock`](crate::sync::Mutex::lock)
|
||||
/// or [`RwLock::read`](crate::sync::RwLock::read).
|
||||
///
|
||||
/// This method may panic if std was built with `panic="abort"`.
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
#[track_caller]
|
||||
pub fn new(_data: T) -> PoisonError<T> {
|
||||
panic!("PoisonError created in a libstd built with panic=\"abort\"")
|
||||
}
|
||||
|
||||
/// Consumes this error indicating that a lock is poisoned, returning the
|
||||
/// associated data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashSet;
|
||||
/// use std::sync::{Arc, Mutex};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let mutex = Arc::new(Mutex::new(HashSet::new()));
|
||||
///
|
||||
/// // poison the mutex
|
||||
/// let c_mutex = Arc::clone(&mutex);
|
||||
/// let _ = thread::spawn(move || {
|
||||
/// let mut data = c_mutex.lock().unwrap();
|
||||
/// data.insert(10);
|
||||
/// panic!();
|
||||
/// }).join();
|
||||
///
|
||||
/// let p_err = mutex.lock().unwrap_err();
|
||||
/// let data = p_err.into_inner();
|
||||
/// println!("recovered {} items", data.len());
|
||||
/// ```
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// mutable reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> From<PoisonError<T>> for TryLockError<T> {
|
||||
fn from(err: PoisonError<T>) -> TryLockError<T> {
|
||||
TryLockError::Poisoned(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Debug for TryLockError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
TryLockError::WouldBlock => "WouldBlock".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> fmt::Display for TryLockError<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
TryLockError::WouldBlock => "try_lock failed because the operation would block",
|
||||
}
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Error for TryLockError<T> {
|
||||
#[allow(deprecated)]
|
||||
fn cause(&self) -> Option<&dyn Error> {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(ref p) => Some(p),
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_result<T, U, F>(result: LockResult<T>, f: F) -> LockResult<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
match result {
|
||||
Ok(t) => Ok(f(t)),
|
||||
#[cfg(panic = "unwind")]
|
||||
Err(PoisonError { data }) => Err(PoisonError::new(f(data))),
|
||||
}
|
||||
}
|
||||
@@ -1,510 +0,0 @@
|
||||
use crate::fmt;
|
||||
use crate::sync::WaitTimeoutResult;
|
||||
use crate::sync::poison::{self, LockResult, MutexGuard, PoisonError, mutex};
|
||||
use crate::sys::sync as sys;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
/// A Condition Variable
|
||||
///
|
||||
/// Condition variables represent the ability to block a thread such that it
|
||||
/// consumes no CPU time while waiting for an event to occur. Condition
|
||||
/// variables are typically associated with a boolean predicate (a condition)
|
||||
/// and a mutex. The predicate is always verified inside of the mutex before
|
||||
/// determining that a thread must block.
|
||||
///
|
||||
/// Functions in this module will block the current **thread** of execution.
|
||||
/// Note that any attempt to use multiple mutexes on the same condition
|
||||
/// variable may result in a runtime panic.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// // Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Condvar {
|
||||
inner: sys::Condvar,
|
||||
}
|
||||
|
||||
impl Condvar {
|
||||
/// Creates a new condition variable which is ready to be waited on and
|
||||
/// notified.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::Condvar;
|
||||
///
|
||||
/// let condvar = Condvar::new();
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_stable(feature = "const_locks", since = "1.63.0")]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn new() -> Condvar {
|
||||
Condvar { inner: sys::Condvar::new() }
|
||||
}
|
||||
|
||||
/// Blocks the current thread until this condition variable receives a
|
||||
/// notification.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// Note that this function is susceptible to spurious wakeups. Condition
|
||||
/// variables normally have a boolean predicate associated with them, and
|
||||
/// the predicate must always be checked each time this function returns to
|
||||
/// protect against spurious wakeups.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the mutex being waited on is
|
||||
/// poisoned when this thread re-acquires the lock. For more information,
|
||||
/// see information about [poisoning] on the [`Mutex`] type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may [`panic!`] if it is used with more than one mutex
|
||||
/// over time.
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
/// [poisoning]: super::Mutex#poisoning
|
||||
/// [`Mutex`]: super::Mutex
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
|
||||
let poisoned = unsafe {
|
||||
let lock = mutex::guard_lock(&guard);
|
||||
self.inner.wait(lock);
|
||||
mutex::guard_poison(&guard).get()
|
||||
};
|
||||
if poisoned { Err(PoisonError::new(guard)) } else { Ok(guard) }
|
||||
}
|
||||
|
||||
/// Blocks the current thread until the provided condition becomes false.
|
||||
///
|
||||
/// `condition` is checked immediately; if not met (returns `true`), this
|
||||
/// will [`wait`] for the next notification then check again. This repeats
|
||||
/// until `condition` returns `false`, in which case this function returns.
|
||||
///
|
||||
/// This function will atomically unlock the mutex specified (represented by
|
||||
/// `guard`) and block the current thread. This means that any calls
|
||||
/// to [`notify_one`] or [`notify_all`] which happen logically after the
|
||||
/// mutex is unlocked are candidates to wake this thread up. When this
|
||||
/// function call returns, the lock specified will have been re-acquired.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the mutex being waited on is
|
||||
/// poisoned when this thread re-acquires the lock. For more information,
|
||||
/// see information about [poisoning] on the [`Mutex`] type.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
/// [poisoning]: super::Mutex#poisoning
|
||||
/// [`Mutex`]: super::Mutex
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock().unwrap();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
|
||||
/// let _guard = cvar.wait_while(lock.lock().unwrap(), |pending| { *pending }).unwrap();
|
||||
/// ```
|
||||
#[stable(feature = "wait_until", since = "1.42.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_while<'a, T, F>(
|
||||
&self,
|
||||
mut guard: MutexGuard<'a, T>,
|
||||
mut condition: F,
|
||||
) -> LockResult<MutexGuard<'a, T>>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
while condition(&mut *guard) {
|
||||
guard = self.wait(guard)?;
|
||||
}
|
||||
Ok(guard)
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`]
|
||||
/// except that the thread will be blocked for roughly no longer
|
||||
/// than `ms` milliseconds. This method should not be used for
|
||||
/// precise timing due to anomalies such as preemption or platform
|
||||
/// differences that might not cause the maximum amount of time
|
||||
/// waited to be precisely `ms`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned boolean is `false` only if the timeout is known
|
||||
/// to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will be re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout_ms(started, 10).unwrap();
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
#[deprecated(since = "1.6.0", note = "replaced by `std::sync::Condvar::wait_timeout`")]
|
||||
pub fn wait_timeout_ms<'a, T>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
ms: u32,
|
||||
) -> LockResult<(MutexGuard<'a, T>, bool)> {
|
||||
let res = self.wait_timeout(guard, Duration::from_millis(ms as u64));
|
||||
poison::map_result(res, |(a, b)| (a, !b.timed_out()))
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait`] except that
|
||||
/// the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time. This function is susceptible to spurious wakeups.
|
||||
/// Condition variables normally have a boolean predicate associated with
|
||||
/// them, and the predicate must always be checked each time this function
|
||||
/// returns to protect against spurious wakeups. Furthermore, since the timeout
|
||||
/// is given relative to the moment this function is called, it needs to be adjusted
|
||||
/// when this function is called in a loop. The [`wait_timeout_while`] method
|
||||
/// lets you wait with a timeout while a predicate is true, taking care of all these concerns.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will be re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout_while`]: Self::wait_timeout_while
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // as long as the value inside the `Mutex<bool>` is `false`, we wait
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap();
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "wait_timeout", since = "1.5.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_timeout<'a, T>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
dur: Duration,
|
||||
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
|
||||
let (poisoned, result) = unsafe {
|
||||
let lock = mutex::guard_lock(&guard);
|
||||
let success = self.inner.wait_timeout(lock, dur);
|
||||
(mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success))
|
||||
};
|
||||
if poisoned { Err(PoisonError::new((guard, result))) } else { Ok((guard, result)) }
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
/// specified duration.
|
||||
///
|
||||
/// The semantics of this function are equivalent to [`wait_while`] except
|
||||
/// that the thread will be blocked for roughly no longer than `dur`. This
|
||||
/// method should not be used for precise timing due to anomalies such as
|
||||
/// preemption or platform differences that might not cause the maximum
|
||||
/// amount of time waited to be precisely `dur`.
|
||||
///
|
||||
/// Note that the best effort is made to ensure that the time waited is
|
||||
/// measured with a monotonic clock, and not affected by the changes made to
|
||||
/// the system time.
|
||||
///
|
||||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed without the condition being met.
|
||||
///
|
||||
/// Like [`wait_while`], the lock specified will be re-acquired when this
|
||||
/// function returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait_while`]: Self::wait_while
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(true), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut pending = lock.lock().unwrap();
|
||||
/// *pending = false;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let result = cvar.wait_timeout_while(
|
||||
/// lock.lock().unwrap(),
|
||||
/// Duration::from_millis(100),
|
||||
/// |&mut pending| pending,
|
||||
/// ).unwrap();
|
||||
/// if result.1.timed_out() {
|
||||
/// // timed-out without the condition ever evaluating to false.
|
||||
/// }
|
||||
/// // access the locked mutex via result.0
|
||||
/// ```
|
||||
#[stable(feature = "wait_timeout_until", since = "1.42.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn wait_timeout_while<'a, T, F>(
|
||||
&self,
|
||||
mut guard: MutexGuard<'a, T>,
|
||||
dur: Duration,
|
||||
mut condition: F,
|
||||
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)>
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if !condition(&mut *guard) {
|
||||
return Ok((guard, WaitTimeoutResult(false)));
|
||||
}
|
||||
let timeout = match dur.checked_sub(start.elapsed()) {
|
||||
Some(timeout) => timeout,
|
||||
None => return Ok((guard, WaitTimeoutResult(true))),
|
||||
};
|
||||
guard = self.wait_timeout(guard, timeout)?.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Wakes up one blocked thread on this condvar.
|
||||
///
|
||||
/// If there is a blocked thread on this condition variable, then it will
|
||||
/// be woken up from its call to [`wait`] or [`wait_timeout`]. Calls to
|
||||
/// `notify_one` are not buffered in any way.
|
||||
///
|
||||
/// To wake up all threads, see [`notify_all`].
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
/// [`wait_timeout`]: Self::wait_timeout
|
||||
/// [`notify_all`]: Self::notify_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_one();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn notify_one(&self) {
|
||||
self.inner.notify_one()
|
||||
}
|
||||
|
||||
/// Wakes up all blocked threads on this condvar.
|
||||
///
|
||||
/// This method will ensure that any current waiters on the condition
|
||||
/// variable are awoken. Calls to `notify_all()` are not buffered in any
|
||||
/// way.
|
||||
///
|
||||
/// To wake up only one thread, see [`notify_one`].
|
||||
///
|
||||
/// [`notify_one`]: Self::notify_one
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Mutex, Condvar};
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
/// let pair2 = Arc::clone(&pair);
|
||||
///
|
||||
/// thread::spawn(move || {
|
||||
/// let (lock, cvar) = &*pair2;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// *started = true;
|
||||
/// // We notify the condvar that the value has changed.
|
||||
/// cvar.notify_all();
|
||||
/// });
|
||||
///
|
||||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock().unwrap();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started).unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_should_not_be_called_on_const_items]
|
||||
pub fn notify_all(&self) {
|
||||
self.inner.notify_all()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "std_debug", since = "1.16.0")]
|
||||
impl fmt::Debug for Condvar {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Condvar").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "condvar_default", since = "1.10.0")]
|
||||
impl Default for Condvar {
|
||||
/// Creates a `Condvar` which is ready to be waited on and notified.
|
||||
fn default() -> Condvar {
|
||||
Condvar::new()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user