Rollup merge of #155250 - ChrisDenton:pipe-fs, r=Mark-Simulacrum

Windows: Cache the pipe filesystem handle

Updates the anonymous pipe handling based on feedback from @lhecker  (see https://github.com/rust-lang/rust/pull/142517#discussion_r3065864262). This does two things:

1. Cache the handle to the pipe filesystem so we don't have to reopen it each time.
2. Use the `\Device\NamedPipe\` directly instead of the symlink to it.
This commit is contained in:
Jonathan Brouwer
2026-04-26 19:06:24 +02:00
committed by GitHub
@@ -1,6 +1,8 @@
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
use crate::ops::Neg;
use crate::os::windows::prelude::*;
use crate::sync::atomic::Atomic;
use crate::sync::atomic::Ordering::Relaxed;
use crate::sys::handle::Handle;
use crate::sys::{FromInner, IntoInner, api, c};
use crate::{mem, ptr};
@@ -70,10 +72,19 @@ pub(super) fn child_pipe(ours_readable: bool, their_handle_inheritable: bool) ->
let mut object_attributes = c::OBJECT_ATTRIBUTES::default();
object_attributes.Length = size_of::<c::OBJECT_ATTRIBUTES>() as u32;
// Open a handle to the pipe filesystem (`\??\PIPE\`).
// This will be used when creating a new annon pipe.
let pipe_fs = {
let path = api::unicode_str!(r"\??\PIPE\");
// Open a handle to the pipe filesystem (`\Device\NamedPipe\`).
// This will be used when creating a new anonymous pipe.
//
// We cache the handle once so we can reuse it without needing to reopen it each time.
// NOTE: this means the handle may appear to be leaked but that's fine because
// it's only one handle and the OS will clean it up when the process exits.
static PIPE_FS: Atomic<c::HANDLE> = Atomic::<c::HANDLE>::new(ptr::null_mut());
let pipe_fs = if let handle = PIPE_FS.load(Relaxed)
&& !handle.is_null()
{
handle
} else {
let path = api::unicode_str!(r"\Device\NamedPipe\");
object_attributes.ObjectName = path.as_ptr();
let mut pipe_fs = ptr::null_mut();
let status = c::NtOpenFile(
@@ -85,7 +96,13 @@ pub(super) fn child_pipe(ours_readable: bool, their_handle_inheritable: bool) ->
c::FILE_SYNCHRONOUS_IO_NONALERT, // synchronous access
);
if c::nt_success(status) {
Handle::from_raw_handle(pipe_fs)
match PIPE_FS.compare_exchange(ptr::null_mut(), pipe_fs, Relaxed, Relaxed) {
Ok(_) => pipe_fs,
Err(existing) => {
c::CloseHandle(pipe_fs);
existing
}
}
} else {
return Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as i32));
}
@@ -104,7 +121,7 @@ pub(super) fn child_pipe(ours_readable: bool, their_handle_inheritable: bool) ->
let ours = {
// Use the pipe filesystem as the root directory.
// With no name provided, an anonymous pipe will be created.
object_attributes.RootDirectory = pipe_fs.as_raw_handle();
object_attributes.RootDirectory = pipe_fs;
// A negative timeout value is a relative time (rather than an absolute time).
// The time is given in 100's of nanoseconds so this is 50 milliseconds.