-
-
Notifications
You must be signed in to change notification settings - Fork 14.8k
Windows: Cache the pipe filesystem handle #155250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, this feels like it needs to be Acquire coupled with a Release store below, no? Otherwise we're not necessarily seeing the memory initialization. Though probably OK in practice given the interesting memory is in the kernel... right?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah exactly: The NT file handle is kernel-mode only and so access is fully synchronized already.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, all we need to do here is ensure the atomic containing the handle synchronises with itself. For which relaxed is sufficient. It would be a (rather serious, imo) bug in the OS if using a handle across threads risked synchronisation issues around the handle itself. |
||
| && !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. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we going to get complaints from valgrind (not sure that works on Windows) for a handle leak due to this? Anything preemptive that we could do to indicate it's intentional?
View changes since the review
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't tend to get those types of complaints for Windows but. sure, I can add a comment explicitly noting this is intentional.
Edit: Windows doesn't have the tight fd limit that unix systems can have so leaking a single handle is less of an issue (the limit for handles is something north of 16 million).