Skip to content

Commit 69a5ae3

Browse files
committed
Auto merge of #95841 - ChrisDenton:pipe-server, r=m-ou-se
Windows: Use a pipe relay for chaining pipes Fixes #95759 This fixes the issue by chaining pipes synchronously and manually pumping messages between them. It's not ideal but it has the advantage of not costing anything if pipes are not chained ("don't pay for what you don't use") and it also avoids breaking existing code that rely on our end of the pipe being asynchronous (which includes rustc's own testing framework). Libraries can avoid needing this by using their own pipes to chain commands.
2 parents e7575f9 + 9013054 commit 69a5ae3

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

library/std/src/sys/windows/pipe.rs

+43
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,46 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
165165
}
166166
}
167167

168+
/// Takes an asynchronous source pipe and returns a synchronous pipe suitable
169+
/// for sending to a child process.
170+
///
171+
/// This is achieved by creating a new set of pipes and spawning a thread that
172+
/// relays messages between the source and the synchronous pipe.
173+
pub fn spawn_pipe_relay(
174+
source: &AnonPipe,
175+
ours_readable: bool,
176+
their_handle_inheritable: bool,
177+
) -> io::Result<AnonPipe> {
178+
// We need this handle to live for the lifetime of the thread spawned below.
179+
let source = source.duplicate()?;
180+
181+
// create a new pair of anon pipes.
182+
let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?;
183+
184+
// Spawn a thread that passes messages from one pipe to the other.
185+
// Any errors will simply cause the thread to exit.
186+
let (reader, writer) = if ours_readable { (ours, source) } else { (source, ours) };
187+
crate::thread::spawn(move || {
188+
let mut buf = [0_u8; 4096];
189+
'reader: while let Ok(len) = reader.read(&mut buf) {
190+
if len == 0 {
191+
break;
192+
}
193+
let mut start = 0;
194+
while let Ok(written) = writer.write(&buf[start..len]) {
195+
start += written;
196+
if start == len {
197+
continue 'reader;
198+
}
199+
}
200+
break;
201+
}
202+
});
203+
204+
// Return the pipe that should be sent to the child process.
205+
Ok(theirs)
206+
}
207+
168208
fn random_number() -> usize {
169209
static N: AtomicUsize = AtomicUsize::new(0);
170210
loop {
@@ -192,6 +232,9 @@ impl AnonPipe {
192232
pub fn into_handle(self) -> Handle {
193233
self.inner
194234
}
235+
fn duplicate(&self) -> io::Result<Self> {
236+
self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner })
237+
}
195238

196239
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
197240
let result = unsafe {

library/std/src/sys/windows/process.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ pub enum Stdio {
172172
Inherit,
173173
Null,
174174
MakePipe,
175+
Pipe(AnonPipe),
175176
Handle(Handle),
176177
}
177178

@@ -528,6 +529,11 @@ impl Stdio {
528529
Ok(pipes.theirs.into_handle())
529530
}
530531

532+
Stdio::Pipe(ref source) => {
533+
let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
534+
pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle)
535+
}
536+
531537
Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS),
532538

533539
// Open up a reference to NUL with appropriate read/write
@@ -552,7 +558,7 @@ impl Stdio {
552558

553559
impl From<AnonPipe> for Stdio {
554560
fn from(pipe: AnonPipe) -> Stdio {
555-
Stdio::Handle(pipe.into_handle())
561+
Stdio::Pipe(pipe)
556562
}
557563
}
558564

0 commit comments

Comments
 (0)