Skip to content

Commit 4ae6c83

Browse files
committed
Add some hacks to get stdin piping working more correctly in windows
The way pipes work in windows is not the same as unix, though I'm not entirely clear on the differences. This patch changes the windows pipe method to return non-inheritable fds, and the windows rust_run_program method to duplicate them before spawning the new process. This allows make-check-pretty to work on windows.
1 parent e33de59 commit 4ae6c83

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

src/lib/win32_os.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ mod libc_constants {
3838
fn O_TRUNC() -> int { ret 512; }
3939
fn O_TEXT() -> int { ret 16384; }
4040
fn O_BINARY() -> int { ret 32768; }
41+
fn O_NOINHERIT() -> int { ret 0x0080; }
4142
fn S_IRUSR() -> uint {
4243
ret 256u; // really _S_IREAD in win32
4344

@@ -60,9 +61,18 @@ fn target_os() -> str { ret "win32"; }
6061
fn dylib_filename(base: str) -> str { ret base + ".dll"; }
6162

6263
fn pipe() -> {in: int, out: int} {
64+
// Windows pipes work subtly differently than unix pipes, and their
65+
// inheritance has to be handled in a different way that I don't fully
66+
// understand. Here we explicitly make the pipe non-inheritable,
67+
// which means to pass it to a subprocess they need to be duplicated
68+
// first, as in rust_run_program.
6369
let fds = {mutable in: 0, mutable out: 0};
64-
assert (os::libc::_pipe(ptr::addr_of(fds.in), 1024u,
65-
libc_constants::O_BINARY()) == 0);
70+
let res = os::libc::_pipe(ptr::addr_of(fds.in), 1024u,
71+
libc_constants::O_BINARY()
72+
| libc_constants::O_NOINHERIT());
73+
assert res == 0;
74+
assert fds.in != -1 && fds.in != 0;
75+
assert fds.out != -1 && fds.in != 0;
6676
ret {in: fds.in, out: fds.out};
6777
}
6878

src/rt/rust_run_program.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,20 @@ rust_run_program(void* task, const char* argv[],
1212
ZeroMemory(&si, sizeof(STARTUPINFO));
1313
si.cb = sizeof(STARTUPINFO);
1414
si.dwFlags = STARTF_USESTDHANDLES;
15-
si.hStdInput = (HANDLE)_get_osfhandle(in_fd ? in_fd : 0);
16-
si.hStdOutput = (HANDLE)_get_osfhandle(out_fd ? out_fd : 1);
17-
si.hStdError = (HANDLE)_get_osfhandle(err_fd ? err_fd : 2);
15+
16+
HANDLE curproc = GetCurrentProcess();
17+
HANDLE origStdin = (HANDLE)_get_osfhandle(in_fd ? in_fd : 0);
18+
if (!DuplicateHandle(curproc, origStdin,
19+
curproc, &si.hStdInput, 0, 1, DUPLICATE_SAME_ACCESS))
20+
return -1;
21+
HANDLE origStdout = (HANDLE)_get_osfhandle(out_fd ? out_fd : 1);
22+
if (!DuplicateHandle(curproc, origStdout,
23+
curproc, &si.hStdOutput, 0, 1, DUPLICATE_SAME_ACCESS))
24+
return -1;
25+
HANDLE origStderr = (HANDLE)_get_osfhandle(err_fd ? err_fd : 2);
26+
if (!DuplicateHandle(curproc, origStderr,
27+
curproc, &si.hStdError, 0, 1, DUPLICATE_SAME_ACCESS))
28+
return -1;
1829

1930
size_t cmd_len = 0;
2031
for (const char** arg = argv; *arg; arg++) {
@@ -32,6 +43,10 @@ rust_run_program(void* task, const char* argv[],
3243
PROCESS_INFORMATION pi;
3344
BOOL created = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
3445
0, NULL, NULL, &si, &pi);
46+
47+
CloseHandle(si.hStdInput);
48+
CloseHandle(si.hStdOutput);
49+
CloseHandle(si.hStdError);
3550
free(cmd);
3651

3752
if (!created) return -1;

src/test/stdtest/run.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
use std;
22
import std::run;
3+
import std::os;
4+
import std::io;
5+
import std::option;
6+
import std::str;
37

48
// Regression test for memory leaks
59
#[cfg(target_os = "linux")]
@@ -15,4 +19,49 @@ fn test_leaks() {
1519
#[cfg(target_os = "win32")]
1620
#[test]
1721
#[ignore]
18-
fn test_leaks() { }
22+
fn test_leaks() { }
23+
24+
#[test]
25+
fn test_pipes() {
26+
let pipe_in = os::pipe();
27+
let pipe_out = os::pipe();
28+
let pipe_err = os::pipe();
29+
30+
let pid = run::spawn_process("cat", [],
31+
pipe_in.in, pipe_out.out, pipe_err.out);
32+
os::libc::close(pipe_in.in);
33+
os::libc::close(pipe_out.out);
34+
os::libc::close(pipe_err.out);
35+
36+
if pid == -1 { fail; }
37+
let expected = "test";
38+
writeclose(pipe_in.out, expected);
39+
let actual = readclose(pipe_out.in);
40+
readclose(pipe_err.in);
41+
os::waitpid(pid);
42+
43+
log expected;
44+
log actual;
45+
assert expected == actual;
46+
47+
fn writeclose(fd: int, s: &str) {
48+
let writer = io::new_writer(
49+
io::fd_buf_writer(fd, option::none));
50+
writer.write_str(s);
51+
52+
os::libc::close(fd);
53+
}
54+
55+
fn readclose(fd: int) -> str {
56+
// Copied from run::program_output
57+
let file = os::fd_FILE(fd);
58+
let reader = io::new_reader(io::FILE_buf_reader(file, option::none));
59+
let buf = "";
60+
while !reader.eof() {
61+
let bytes = reader.read_bytes(4096u);
62+
buf += str::unsafe_from_bytes(bytes);
63+
}
64+
os::libc::fclose(file);
65+
ret buf;
66+
}
67+
}

0 commit comments

Comments
 (0)