2011-07-30 23:11:14 -05:00
|
|
|
// So when running tests in parallel there's a potential race on environment
|
|
|
|
// variables if we let each task spawn its own children - between the time the
|
|
|
|
// environment is set and the process is spawned another task could spawn its
|
|
|
|
// child process. Because of that we have to use a complicated scheme with a
|
|
|
|
// dedicated server for spawning processes.
|
|
|
|
|
|
|
|
import std::option;
|
|
|
|
import std::task;
|
2011-08-13 17:20:11 -05:00
|
|
|
import std::task::task_id;
|
2011-07-30 23:11:14 -05:00
|
|
|
import std::generic_os::setenv;
|
|
|
|
import std::generic_os::getenv;
|
2011-08-15 18:38:23 -05:00
|
|
|
import std::vec;
|
2011-07-30 23:11:14 -05:00
|
|
|
import std::os;
|
|
|
|
import std::run;
|
2011-08-11 21:14:38 -05:00
|
|
|
import std::io;
|
2011-07-30 23:11:14 -05:00
|
|
|
import std::str;
|
2011-08-17 19:13:11 -05:00
|
|
|
import std::comm::chan;
|
|
|
|
import std::comm::port;
|
2011-08-13 17:20:11 -05:00
|
|
|
import std::comm::send;
|
2011-08-17 19:13:11 -05:00
|
|
|
import std::comm::recv;
|
2011-07-30 23:11:14 -05:00
|
|
|
|
|
|
|
export handle;
|
|
|
|
export mk;
|
|
|
|
export from_chan;
|
|
|
|
export run;
|
|
|
|
export close;
|
|
|
|
export reqchan;
|
|
|
|
|
2011-08-17 19:13:11 -05:00
|
|
|
type reqchan = chan<request>;
|
2011-07-30 23:11:14 -05:00
|
|
|
|
2011-08-10 11:27:22 -05:00
|
|
|
type handle = {task: option::t<task_id>, chan: reqchan};
|
2011-07-30 23:11:14 -05:00
|
|
|
|
2011-07-31 17:33:40 -05:00
|
|
|
tag request {
|
2011-08-17 19:13:11 -05:00
|
|
|
exec([u8], [u8], [[u8]], chan<response>);
|
2011-07-31 17:33:40 -05:00
|
|
|
stop;
|
|
|
|
}
|
2011-07-30 23:11:14 -05:00
|
|
|
|
2011-07-31 17:33:40 -05:00
|
|
|
type response = {pid: int, infd: int, outfd: int, errfd: int};
|
2011-07-30 23:11:14 -05:00
|
|
|
|
|
|
|
fn mk() -> handle {
|
2011-08-17 19:13:11 -05:00
|
|
|
let setupport = port();
|
|
|
|
let task = task::spawn(bind fn(setupchan: chan<chan<request>>) {
|
|
|
|
let reqport = port();
|
|
|
|
let reqchan = chan(reqport);
|
2011-08-13 17:20:11 -05:00
|
|
|
send(setupchan, reqchan);
|
2011-07-30 23:11:14 -05:00
|
|
|
worker(reqport);
|
2011-08-17 19:13:11 -05:00
|
|
|
} (chan(setupport)));
|
2011-07-30 23:11:14 -05:00
|
|
|
ret {task: option::some(task),
|
2011-08-17 19:13:11 -05:00
|
|
|
chan: recv(setupport)
|
2011-07-30 23:11:14 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_chan(ch: &reqchan) -> handle { {task: option::none, chan: ch} }
|
|
|
|
|
|
|
|
fn close(handle: &handle) {
|
2011-08-13 17:20:11 -05:00
|
|
|
send(handle.chan, stop);
|
|
|
|
task::join_id(option::get(handle.task));
|
2011-07-30 23:11:14 -05:00
|
|
|
}
|
|
|
|
|
2011-07-31 17:33:40 -05:00
|
|
|
fn run(handle: &handle, lib_path: &str,
|
2011-08-10 11:27:22 -05:00
|
|
|
prog: &str, args: &[str], input: &option::t<str>) ->
|
2011-07-30 23:11:14 -05:00
|
|
|
{status: int, out: str, err: str} {
|
2011-08-17 19:13:11 -05:00
|
|
|
let p = port();
|
|
|
|
let ch = chan(p);
|
2011-08-13 17:20:11 -05:00
|
|
|
send(handle.chan, exec(str::bytes(lib_path),
|
|
|
|
str::bytes(prog),
|
2011-08-18 16:32:25 -05:00
|
|
|
clone_vecstr(args),
|
2011-08-13 17:20:11 -05:00
|
|
|
ch));
|
2011-08-17 19:13:11 -05:00
|
|
|
let resp = recv(p);
|
2011-07-31 17:33:40 -05:00
|
|
|
|
|
|
|
writeclose(resp.infd, input);
|
2011-07-30 23:11:14 -05:00
|
|
|
let output = readclose(resp.outfd);
|
|
|
|
let errput = readclose(resp.errfd);
|
|
|
|
let status = os::waitpid(resp.pid);
|
|
|
|
ret {status: status, out: output, err: errput};
|
|
|
|
}
|
|
|
|
|
2011-08-10 11:27:22 -05:00
|
|
|
fn writeclose(fd: int, s: &option::t<str>) {
|
2011-07-31 17:33:40 -05:00
|
|
|
if option::is_some(s) {
|
2011-08-11 21:14:38 -05:00
|
|
|
let writer = io::new_writer(
|
|
|
|
io::fd_buf_writer(fd, option::none));
|
2011-07-31 17:33:40 -05:00
|
|
|
writer.write_str(option::get(s));
|
|
|
|
}
|
|
|
|
|
|
|
|
os::libc::close(fd);
|
|
|
|
}
|
|
|
|
|
2011-07-30 23:11:14 -05:00
|
|
|
fn readclose(fd: int) -> str {
|
|
|
|
// Copied from run::program_output
|
|
|
|
let file = os::fd_FILE(fd);
|
2011-08-11 21:14:38 -05:00
|
|
|
let reader = io::new_reader(
|
|
|
|
io::FILE_buf_reader(file, option::none));
|
2011-07-30 23:11:14 -05:00
|
|
|
let buf = "";
|
|
|
|
while !reader.eof() {
|
2011-08-11 20:49:36 -05:00
|
|
|
let bytes = reader.read_bytes(4096u);
|
2011-08-11 19:13:53 -05:00
|
|
|
buf += str::unsafe_from_bytes(bytes);
|
2011-07-30 23:11:14 -05:00
|
|
|
}
|
|
|
|
os::libc::fclose(file);
|
|
|
|
ret buf;
|
|
|
|
}
|
|
|
|
|
2011-08-17 19:13:11 -05:00
|
|
|
fn worker(p: port<request>) {
|
2011-07-30 23:11:14 -05:00
|
|
|
|
|
|
|
// FIXME (787): If we declare this inside of the while loop and then
|
|
|
|
// break out of it before it's ever initialized (i.e. we don't run
|
2011-08-17 19:13:11 -05:00
|
|
|
// any tests), then the cleanups will puke.
|
|
|
|
let execparms;
|
2011-07-30 23:11:14 -05:00
|
|
|
|
|
|
|
while true {
|
|
|
|
// FIXME: Sending strings across channels seems to still
|
|
|
|
// leave them refed on the sender's end, which causes problems if
|
|
|
|
// the receiver's poniters outlive the sender's. Here we clone
|
|
|
|
// everything and let the originals go out of scope before sending
|
|
|
|
// a response.
|
|
|
|
execparms = {
|
|
|
|
// FIXME (785): The 'discriminant' of an alt expression has
|
|
|
|
// the same scope as the alt expression itself, so we have to
|
|
|
|
// put the entire alt in another block to make sure the exec
|
|
|
|
// message goes out of scope. Seems like the scoping rules for
|
|
|
|
// the alt discriminant are wrong.
|
2011-08-17 19:13:11 -05:00
|
|
|
alt recv(p) {
|
2011-07-30 23:11:14 -05:00
|
|
|
exec(lib_path, prog, args, respchan) {
|
|
|
|
{
|
2011-08-13 17:20:11 -05:00
|
|
|
lib_path: str::unsafe_from_bytes(lib_path),
|
|
|
|
prog: str::unsafe_from_bytes(prog),
|
2011-08-18 16:32:25 -05:00
|
|
|
args: clone_vecu8str(args),
|
2011-07-30 23:11:14 -05:00
|
|
|
respchan: respchan
|
|
|
|
}
|
|
|
|
}
|
|
|
|
stop. { ret }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// This is copied from run::start_program
|
|
|
|
let pipe_in = os::pipe();
|
|
|
|
let pipe_out = os::pipe();
|
|
|
|
let pipe_err = os::pipe();
|
|
|
|
let spawnproc =
|
|
|
|
bind run::spawn_process(execparms.prog,
|
2011-08-12 00:31:45 -05:00
|
|
|
execparms.args,
|
2011-07-30 23:11:14 -05:00
|
|
|
pipe_in.in,
|
|
|
|
pipe_out.out,
|
|
|
|
pipe_err.out);
|
|
|
|
let pid = with_lib_path(execparms.lib_path, spawnproc);
|
2011-07-31 17:33:40 -05:00
|
|
|
|
2011-07-30 23:11:14 -05:00
|
|
|
os::libc::close(pipe_in.in);
|
|
|
|
os::libc::close(pipe_out.out);
|
|
|
|
os::libc::close(pipe_err.out);
|
|
|
|
if pid == -1 {
|
2011-07-31 17:33:40 -05:00
|
|
|
os::libc::close(pipe_in.out);
|
2011-07-30 23:11:14 -05:00
|
|
|
os::libc::close(pipe_out.in);
|
|
|
|
os::libc::close(pipe_err.in);
|
|
|
|
fail;
|
|
|
|
}
|
2011-07-31 17:33:40 -05:00
|
|
|
|
2011-08-13 17:20:11 -05:00
|
|
|
send(execparms.respchan,
|
|
|
|
{pid: pid,
|
|
|
|
infd: pipe_in.out,
|
|
|
|
outfd: pipe_out.in,
|
|
|
|
errfd: pipe_err.in});
|
2011-07-30 23:11:14 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-12 08:37:25 -05:00
|
|
|
fn with_lib_path<T>(path: &str, f: fn() -> T ) -> T {
|
2011-07-30 23:11:14 -05:00
|
|
|
let maybe_oldpath = getenv(util::lib_path_env_var());
|
|
|
|
append_lib_path(path);
|
|
|
|
let res = f();
|
|
|
|
if option::is_some(maybe_oldpath) {
|
|
|
|
export_lib_path(option::get(maybe_oldpath));
|
|
|
|
} else {
|
|
|
|
// FIXME: This should really be unset but we don't have that yet
|
|
|
|
export_lib_path("");
|
|
|
|
}
|
|
|
|
ret res;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn append_lib_path(path: &str) { export_lib_path(util::make_new_path(path)); }
|
|
|
|
|
|
|
|
fn export_lib_path(path: &str) { setenv(util::lib_path_env_var(), path); }
|
|
|
|
|
2011-08-18 16:32:25 -05:00
|
|
|
fn clone_vecstr(v: &[str]) -> [[u8]] {
|
2011-08-15 11:52:18 -05:00
|
|
|
let r = ~[];
|
2011-08-15 18:38:23 -05:00
|
|
|
for t: str in vec::slice(v, 0u, vec::len(v)) {
|
2011-08-15 11:52:18 -05:00
|
|
|
r += ~[str::bytes(t)];
|
2011-08-13 17:20:11 -05:00
|
|
|
}
|
|
|
|
ret r;
|
2011-07-30 23:11:14 -05:00
|
|
|
}
|
|
|
|
|
2011-08-18 16:32:25 -05:00
|
|
|
fn clone_vecu8str(v: &[[u8]]) -> [str] {
|
2011-08-15 11:52:18 -05:00
|
|
|
let r = ~[];
|
2011-08-15 18:38:23 -05:00
|
|
|
for t in vec::slice(v, 0u, vec::len(v)) {
|
2011-08-15 11:52:18 -05:00
|
|
|
r += ~[str::unsafe_from_bytes(t)];
|
2011-07-30 23:11:14 -05:00
|
|
|
}
|
|
|
|
ret r;
|
|
|
|
}
|