Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c516c28
Add useless prefix `try_into_` for suggest_name
A4-Tacks Dec 28, 2025
72c6b07
Migrate `move_arm_cond_to_match_guard` assist to use `SyntaxEditor`
A4-Tacks Dec 30, 2025
862ab4c
remove unwanted comment
Shourya742 Dec 31, 2025
ec7db07
Allow finding references from doc comments
SomeoneToIgnore Dec 31, 2025
2a7e3e6
add bidirectional flow for file method
Shourya742 Dec 31, 2025
abbf027
add bidirectional flow for local file method
Shourya742 Dec 31, 2025
c4dd25f
add roundtrip abstraction to remove subrequest subresponse boilerplat…
Shourya742 Dec 31, 2025
019d016
Merge pull request #21376 from SomeoneToIgnore/doc-find-defs
Veykril Dec 31, 2025
cde301f
Bump qs from 6.14.0 to 6.14.1 in /editors/code
dependabot[bot] Jan 1, 2026
6394373
add nits
Shourya742 Jan 2, 2026
e34c265
Merge pull request #21361 from A4-Tacks/sugg-name-prefix-try-into
ShoyuVanilla Jan 2, 2026
644eff6
Merge pull request #21383 from rust-lang/dependabot/npm_and_yarn/edit…
ShoyuVanilla Jan 2, 2026
4b34f4f
minor: Fix some minor FIXMEs
Veykril Jan 2, 2026
854f1f8
Merge pull request #21387 from Veykril/push-slxpzqlrpuxt
Veykril Jan 2, 2026
cd7a58e
Pre-allocate intern storages with 64kb of data
Veykril Jan 2, 2026
f841758
Remove unnecessary `ConstLiteralRef` enum
Veykril Jan 2, 2026
94ed628
Merge pull request #21390 from Veykril/push-xkollqttmmrq
Veykril Jan 2, 2026
0598fc6
Merge pull request #21388 from Veykril/push-uyrsywszqotk
Veykril Jan 2, 2026
8d5a7b6
Reduce `impl_signature` query dependencies in method resolution
Veykril Jan 2, 2026
0451cc0
fix: add location links for generic type parameters in inlay hints
benodiwal Jan 2, 2026
1222bbf
feat: added tests
benodiwal Jan 2, 2026
dd97e41
for filename, use localfilename first and then resort to other constr…
Shourya742 Jan 2, 2026
3114dec
fix: add location links for type, const, and lifetime parameters in i…
benodiwal Jan 2, 2026
6326896
emit same info via localfilename and filename
Shourya742 Jan 3, 2026
2483775
Merge pull request #21393 from benodiwal/fix/generic-param-inlay-hint…
Veykril Jan 3, 2026
db43eb0
Merge pull request #21391 from Veykril/push-zttspnlnqpto
Veykril Jan 3, 2026
c56c36e
use fullpath instead of filename
Shourya742 Jan 3, 2026
dfc7440
Merge pull request #21377 from Shourya742/2025-12-31-add-more-proc-ma…
Veykril Jan 3, 2026
7af3130
perf: Only compute lang items for `#![feature(lang_items)]` crates
Veykril Jan 3, 2026
f5aa705
Merge pull request #21396 from Veykril/push-vowxzulyqvuu
Veykril Jan 3, 2026
c99f9c2
Replace sourcedb with expanddb
Shourya742 Jan 3, 2026
38b2e92
added serde to span
Shourya742 Jan 3, 2026
3941fed
add span to proc-macro-srv-cliy
Shourya742 Jan 3, 2026
be566f4
refactor subreq/resp variants to carry span
Shourya742 Jan 3, 2026
4f5502e
Add span to callbacks and correct the source_text implementation
Shourya742 Jan 3, 2026
ce19860
remove span related API from proc-macro-srv/cli/api and remove span f…
Shourya742 Jan 3, 2026
f3b06a1
remove serde from span
Shourya742 Jan 3, 2026
bfe82cb
Merge pull request #21397 from Shourya742/2026-01-03-fix-source-text
Veykril Jan 4, 2026
d4b9ea2
internal: Clean up proc-macro-srv callback trait
Veykril Jan 4, 2026
4bfdbb8
Merge pull request #21400 from Veykril/push-kslvlxlpmwwu
Veykril Jan 4, 2026
50b78f1
Add a README.md to proc-macro-srv-cli
Veykril Jan 4, 2026
6acaa27
Merge pull request #21401 from Veykril/push-optyvyworqny
Veykril Jan 4, 2026
b1ed7e6
Merge pull request #21369 from A4-Tacks/migrate-move-guard
ShoyuVanilla Jan 4, 2026
7dbe12e
fix: Suppress false positive missing assoc item diag on specialization
ShoyuVanilla Jan 4, 2026
8ddb153
Merge pull request #21403 from ShoyuVanilla/missing-assoc-specialize
ChayimFriedman2 Jan 4, 2026
0f5cf5e
Unix implementation for stdio set/take/replace
the8472 Jan 4, 2026
bf2308f
use PIDFD_GET_INFO ioctl when available
the8472 Dec 26, 2025
7e6d1cd
Rollup merge of #150412 - the8472:pidfd-spawn, r=tgross35
matthiaskrgr Jan 5, 2026
53b07ad
Rollup merge of #150668 - the8472:stdio-swap, r=Mark-Simulacrum
matthiaskrgr Jan 5, 2026
741489f
Rollup merge of #150702 - lnicola:sync-from-ra, r=lnicola
matthiaskrgr Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions library/std/src/os/linux/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ impl PidFd {
/// Waits for the child to exit completely, returning the status that it exited with.
///
/// Unlike [`Child::wait`] it does not ensure that the stdin handle is closed.
/// Additionally it will not return an `ExitStatus` if the child
/// has already been reaped. Instead an error will be returned.
///
/// Additionally on kernels prior to 6.15 only the first attempt to
/// reap a child will return an ExitStatus, further attempts
/// will return an Error.
///
/// [`Child::wait`]: process::Child::wait
pub fn wait(&self) -> Result<ExitStatus> {
Expand All @@ -77,8 +79,8 @@ impl PidFd {

/// Attempts to collect the exit status of the child if it has already exited.
///
/// Unlike [`Child::try_wait`] this method will return an Error
/// if the child has already been reaped.
/// On kernels prior to 6.15, and unlike [`Child::try_wait`], only the first attempt
/// to reap a child will return an ExitStatus, further attempts will return an Error.
///
/// [`Child::try_wait`]: process::Child::try_wait
pub fn try_wait(&self) -> Result<Option<ExitStatus>> {
Expand Down
128 changes: 128 additions & 0 deletions library/std/src/os/unix/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,137 @@

#![stable(feature = "rust1", since = "1.0.0")]

use crate::io::{self, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, Write};
#[stable(feature = "rust1", since = "1.0.0")]
pub use crate::os::fd::*;
#[allow(unused_imports)] // not used on all targets
use crate::sys::cvt;

// Tests for this module
#[cfg(test)]
mod tests;

#[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")]
pub trait StdioExt: crate::sealed::Sealed {
/// Redirects the stdio file descriptor to point to the file description underpinning `fd`.
///
/// Rust std::io write buffers (if any) are flushed, but other runtimes
/// (e.g. C stdio) or libraries that acquire a clone of the file descriptor
/// will not be aware of this change.
///
/// # Platform-specific behavior
///
/// This is [currently] implemented using
///
/// - `fd_renumber` on wasip1
/// - `dup2` on most unixes
///
/// [currently]: crate::io#platform-specific-behavior
///
/// ```
/// #![feature(stdio_swap)]
/// use std::io::{self, Read, Write};
/// use std::os::unix::io::StdioExt;
///
/// fn main() -> io::Result<()> {
/// let (reader, mut writer) = io::pipe()?;
/// let mut stdin = io::stdin();
/// stdin.set_fd(reader)?;
/// writer.write_all(b"Hello, world!")?;
/// let mut buffer = vec![0; 13];
/// assert_eq!(stdin.read(&mut buffer)?, 13);
/// assert_eq!(&buffer, b"Hello, world!");
/// Ok(())
/// }
/// ```
fn set_fd<T: Into<OwnedFd>>(&mut self, fd: T) -> io::Result<()>;

/// Redirects the stdio file descriptor and returns a new `OwnedFd`
/// backed by the previous file description.
///
/// See [`set_fd()`] for details.
///
/// [`set_fd()`]: StdioExt::set_fd
fn replace_fd<T: Into<OwnedFd>>(&mut self, replace_with: T) -> io::Result<OwnedFd>;

/// Redirects the stdio file descriptor to the null device (`/dev/null`)
/// and returns a new `OwnedFd` backed by the previous file description.
///
/// Programs that communicate structured data via stdio can use this early in `main()` to
/// extract the fds, treat them as other IO types (`File`, `UnixStream`, etc),
/// apply custom buffering or avoid interference from stdio use later in the program.
///
/// See [`set_fd()`] for additional details.
///
/// [`set_fd()`]: StdioExt::set_fd
fn take_fd(&mut self) -> io::Result<OwnedFd>;
}

macro io_ext_impl($stdio_ty:ty, $stdio_lock_ty:ty, $writer:literal) {
#[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")]
impl StdioExt for $stdio_ty {
fn set_fd<T: Into<OwnedFd>>(&mut self, fd: T) -> io::Result<()> {
self.lock().set_fd(fd)
}

fn take_fd(&mut self) -> io::Result<OwnedFd> {
self.lock().take_fd()
}

fn replace_fd<T: Into<OwnedFd>>(&mut self, replace_with: T) -> io::Result<OwnedFd> {
self.lock().replace_fd(replace_with)
}
}

#[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")]
impl StdioExt for $stdio_lock_ty {
fn set_fd<T: Into<OwnedFd>>(&mut self, fd: T) -> io::Result<()> {
#[cfg($writer)]
self.flush()?;
replace_stdio_fd(self.as_fd(), fd.into())
}

fn take_fd(&mut self) -> io::Result<OwnedFd> {
let null = null_fd()?;
let cloned = self.as_fd().try_clone_to_owned()?;
self.set_fd(null)?;
Ok(cloned)
}

fn replace_fd<T: Into<OwnedFd>>(&mut self, replace_with: T) -> io::Result<OwnedFd> {
let cloned = self.as_fd().try_clone_to_owned()?;
self.set_fd(replace_with)?;
Ok(cloned)
}
}
}

io_ext_impl!(Stdout, StdoutLock<'_>, true);
io_ext_impl!(Stdin, StdinLock<'_>, false);
io_ext_impl!(Stderr, StderrLock<'_>, true);

fn null_fd() -> io::Result<OwnedFd> {
let null_dev = crate::fs::OpenOptions::new().read(true).write(true).open("/dev/null")?;
Ok(null_dev.into())
}

/// Replaces the underlying file descriptor with the one from `other`.
/// Does not set CLOEXEC.
fn replace_stdio_fd(this: BorrowedFd<'_>, other: OwnedFd) -> io::Result<()> {
cfg_select! {
all(target_os = "wasi", target_env = "p1") => {
cvt(unsafe { libc::__wasilibc_fd_renumber(other.as_raw_fd(), this.as_raw_fd()) }).map(|_| ())
}
not(any(
target_arch = "wasm32",
target_os = "hermit",
target_os = "trusty",
target_os = "motor"
)) => {
cvt(unsafe {libc::dup2(other.as_raw_fd(), this.as_raw_fd())}).map(|_| ())
}
_ => {
Err(io::Error::UNSUPPORTED_PLATFORM)
}
}
}
98 changes: 77 additions & 21 deletions library/std/src/sys/pal/unix/linux/pidfd.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::io;
use crate::os::fd::{AsRawFd, FromRawFd, RawFd};
use crate::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::sys::fd::FileDesc;
use crate::sys::process::ExitStatus;
use crate::sys::weak::weak;
use crate::sys::{AsInner, FromInner, IntoInner, cvt};

#[cfg(test)]
Expand All @@ -15,6 +16,69 @@ impl PidFd {
self.send_signal(libc::SIGKILL)
}

pub(crate) fn current_process() -> io::Result<PidFd> {
let pid = crate::process::id();
let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) })?;
Ok(unsafe { PidFd::from_raw_fd(pidfd as RawFd) })
}

pub(crate) fn pid(&self) -> io::Result<u32> {
// since kernel 6.13
// https://lore.kernel.org/all/[email protected]/
let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() };
pidfd_info.mask = libc::PIDFD_INFO_PID as u64;
match cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) })
{
Ok(_) => {}
Err(e) if e.raw_os_error() == Some(libc::EINVAL) => {
// kernel doesn't support that ioctl, try the glibc helper that looks at procfs
weak!(
fn pidfd_getpid(pidfd: RawFd) -> libc::pid_t;
);
if let Some(pidfd_getpid) = pidfd_getpid.get() {
let pid: libc::c_int = cvt(unsafe { pidfd_getpid(self.0.as_raw_fd()) })?;
return Ok(pid as u32);
}
return Err(e);
}
Err(e) => return Err(e),
}

Ok(pidfd_info.pid)
}

fn exit_for_reaped_child(&self) -> io::Result<ExitStatus> {
// since kernel 6.15
// https://lore.kernel.org/linux-fsdevel/20250305-work-pidfs-kill_on_last_close-v3-0-c8c3d8361705@kernel.org/T/
let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() };
pidfd_info.mask = libc::PIDFD_INFO_EXIT as u64;
cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) })?;
Ok(ExitStatus::new(pidfd_info.exit_code))
}

fn waitid(&self, options: libc::c_int) -> io::Result<Option<ExitStatus>> {
let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };
let r = cvt(unsafe {
libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, options)
});
match r {
Err(waitid_err) if waitid_err.raw_os_error() == Some(libc::ECHILD) => {
// already reaped
match self.exit_for_reaped_child() {
Ok(exit_status) => return Ok(Some(exit_status)),
Err(_) => return Err(waitid_err),
}
}
Err(e) => return Err(e),
Ok(_) => {}
}
if unsafe { siginfo.si_pid() } == 0 {
Ok(None)
} else {
Ok(Some(ExitStatus::from_waitid_siginfo(siginfo)))
}
}

pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> {
cvt(unsafe {
libc::syscall(
Expand All @@ -29,29 +93,15 @@ impl PidFd {
}

pub fn wait(&self) -> io::Result<ExitStatus> {
let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };
cvt(unsafe {
libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED)
})?;
Ok(ExitStatus::from_waitid_siginfo(siginfo))
let r = self.waitid(libc::WEXITED)?;
match r {
Some(exit_status) => Ok(exit_status),
None => unreachable!("waitid with WEXITED should not return None"),
}
}

pub fn try_wait(&self) -> io::Result<Option<ExitStatus>> {
let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() };

cvt(unsafe {
libc::waitid(
libc::P_PIDFD,
self.0.as_raw_fd() as u32,
&mut siginfo,
libc::WEXITED | libc::WNOHANG,
)
})?;
if unsafe { siginfo.si_pid() } == 0 {
Ok(None)
} else {
Ok(Some(ExitStatus::from_waitid_siginfo(siginfo)))
}
self.waitid(libc::WEXITED | libc::WNOHANG)
}
}

Expand All @@ -78,3 +128,9 @@ impl FromRawFd for PidFd {
Self(FileDesc::from_raw_fd(fd))
}
}

impl IntoRawFd for PidFd {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
38 changes: 25 additions & 13 deletions library/std/src/sys/pal/unix/linux/pidfd/tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use super::PidFd as InternalPidFd;
use crate::assert_matches::assert_matches;
use crate::os::fd::{AsRawFd, RawFd};
use crate::io::ErrorKind;
use crate::os::fd::AsRawFd;
use crate::os::linux::process::{ChildExt, CommandExt as _};
use crate::os::unix::process::{CommandExt as _, ExitStatusExt};
use crate::process::Command;
use crate::sys::AsInner;

#[test]
fn test_command_pidfd() {
Expand Down Expand Up @@ -48,11 +51,22 @@ fn test_command_pidfd() {
let mut cmd = Command::new("false");
let mut child = unsafe { cmd.pre_exec(|| Ok(())) }.create_pidfd(true).spawn().unwrap();

assert!(child.id() > 0 && child.id() < -1i32 as u32);
let id = child.id();

assert!(id > 0 && id < -1i32 as u32, "spawning with pidfd still returns a sane pid");

if pidfd_open_available {
assert!(child.pidfd().is_ok())
}

if let Ok(pidfd) = child.pidfd() {
match pidfd.as_inner().pid() {
Ok(pid) => assert_eq!(pid, id),
Err(e) if e.kind() == ErrorKind::InvalidInput => { /* older kernel */ }
Err(e) => panic!("unexpected error getting pid from pidfd: {}", e),
}
}

child.wait().expect("error waiting on child");
}

Expand All @@ -77,23 +91,21 @@ fn test_pidfd() {
assert_eq!(status.signal(), Some(libc::SIGKILL));

// Trying to wait again for a reaped child is safe since there's no pid-recycling race.
// But doing so will return an error.
// But doing so may return an error.
let res = fd.wait();
assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ECHILD));
match res {
// older kernels
Err(e) if e.raw_os_error() == Some(libc::ECHILD) => {}
// 6.15+
Ok(exit) if exit.signal() == Some(libc::SIGKILL) => {}
other => panic!("expected ECHILD error, got {:?}", other),
}

// Ditto for additional attempts to kill an already-dead child.
let res = fd.kill();
assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ESRCH));
}

fn probe_pidfd_support() -> bool {
// pidfds require the pidfd_open syscall
let our_pid = crate::process::id();
let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) };
if pidfd >= 0 {
unsafe { libc::close(pidfd as RawFd) };
true
} else {
false
}
InternalPidFd::current_process().is_ok()
}
Loading
Loading