From 5dd05009260d7678b8f7b0f3515a515013f9b03a Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 13 Apr 2026 17:37:16 +0000 Subject: [PATCH 1/2] Windows: Cache the pipe filesystem handle Also use the `\Device\NamedPipe\` directly instead of the symlink to it. --- .../std/src/sys/process/windows/child_pipe.rs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/library/std/src/sys/process/windows/child_pipe.rs b/library/std/src/sys/process/windows/child_pipe.rs index b848435ac275..311272fe05fa 100644 --- a/library/std/src/sys/process/windows/child_pipe.rs +++ b/library/std/src/sys/process/windows/child_pipe.rs @@ -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,15 @@ 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::() 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\`) and cache it. + // This will be used when creating a new anonymous pipe. + static PIPE_FS: Atomic = Atomic::::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 +92,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 +117,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. From a6ec2947a3b94bf9b7bb9e9ee2ad0d82f580e069 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 21 Apr 2026 04:15:26 +0000 Subject: [PATCH 2/2] Explicitly note that we're leaking a handle --- library/std/src/sys/process/windows/child_pipe.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/process/windows/child_pipe.rs b/library/std/src/sys/process/windows/child_pipe.rs index 311272fe05fa..8d71e1c61f82 100644 --- a/library/std/src/sys/process/windows/child_pipe.rs +++ b/library/std/src/sys/process/windows/child_pipe.rs @@ -72,8 +72,12 @@ 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::() as u32; - // Open a handle to the pipe filesystem (`\Device\NamedPipe\`) and cache it. + // 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 = Atomic::::new(ptr::null_mut()); let pipe_fs = if let handle = PIPE_FS.load(Relaxed) && !handle.is_null()