2015-02-06 09:42:57 -08:00
|
|
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
2016-02-12 10:29:25 -08:00
|
|
|
use os::windows::prelude::*;
|
2016-02-12 00:17:24 -08:00
|
|
|
|
2016-02-12 10:29:25 -08:00
|
|
|
use ffi::OsStr;
|
2015-02-06 09:42:57 -08:00
|
|
|
use io;
|
2016-02-12 10:29:25 -08:00
|
|
|
use mem;
|
2016-06-24 20:54:52 +02:00
|
|
|
use path::Path;
|
|
|
|
|
use ptr;
|
2016-02-12 10:29:25 -08:00
|
|
|
use slice;
|
2017-11-01 12:32:13 -07:00
|
|
|
use sync::atomic::Ordering::SeqCst;
|
|
|
|
|
use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
|
2015-04-14 11:17:47 -07:00
|
|
|
use sys::c;
|
2016-02-12 10:29:25 -08:00
|
|
|
use sys::fs::{File, OpenOptions};
|
2015-04-14 11:17:47 -07:00
|
|
|
use sys::handle::Handle;
|
2017-11-01 12:32:13 -07:00
|
|
|
use sys::hashmap_random_keys;
|
2015-02-06 09:42:57 -08:00
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Anonymous pipes
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
pub struct AnonPipe {
|
2015-04-14 11:17:47 -07:00
|
|
|
inner: Handle,
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
pub struct Pipes {
|
|
|
|
|
pub ours: AnonPipe,
|
|
|
|
|
pub theirs: AnonPipe,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Although this looks similar to `anon_pipe` in the Unix module it's actually
|
|
|
|
|
/// subtly different. Here we'll return two pipes in the `Pipes` return value,
|
|
|
|
|
/// but one is intended for "us" where as the other is intended for "someone
|
|
|
|
|
/// else".
|
|
|
|
|
///
|
|
|
|
|
/// Currently the only use case for this function is pipes for stdio on
|
|
|
|
|
/// processes in the standard library, so "ours" is the one that'll stay in our
|
|
|
|
|
/// process whereas "theirs" will be inherited to a child.
|
|
|
|
|
///
|
|
|
|
|
/// The ours/theirs pipes are *not* specifically readable or writable. Each
|
|
|
|
|
/// one only supports a read or a write, but which is which depends on the
|
|
|
|
|
/// boolean flag given. If `ours_readable` is true then `ours` is readable where
|
|
|
|
|
/// `theirs` is writable. Conversely if `ours_readable` is false then `ours` is
|
|
|
|
|
/// writable where `theirs` is readable.
|
|
|
|
|
///
|
|
|
|
|
/// Also note that the `ours` pipe is always a handle opened up in overlapped
|
|
|
|
|
/// mode. This means that technically speaking it should only ever be used
|
|
|
|
|
/// with `OVERLAPPED` instances, but also works out ok if it's only ever used
|
|
|
|
|
/// once at a time (which we do indeed guarantee).
|
|
|
|
|
pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> {
|
2016-02-12 10:29:25 -08:00
|
|
|
// Note that we specifically do *not* use `CreatePipe` here because
|
|
|
|
|
// unfortunately the anonymous pipes returned do not support overlapped
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
// operations. Instead, we create a "hopefully unique" name and create a
|
|
|
|
|
// named pipe which has overlapped operations enabled.
|
2016-02-12 10:29:25 -08:00
|
|
|
//
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
// Once we do this, we connect do it as usual via `CreateFileW`, and then
|
|
|
|
|
// we return those reader/writer halves. Note that the `ours` pipe return
|
|
|
|
|
// value is always the named pipe, whereas `theirs` is just the normal file.
|
|
|
|
|
// This should hopefully shield us from child processes which assume their
|
|
|
|
|
// stdout is a named pipe, which would indeed be odd!
|
2016-02-12 10:29:25 -08:00
|
|
|
unsafe {
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
let ours;
|
2016-02-12 10:29:25 -08:00
|
|
|
let mut name;
|
|
|
|
|
let mut tries = 0;
|
2016-11-10 02:00:25 +01:00
|
|
|
let mut reject_remote_clients_flag = c::PIPE_REJECT_REMOTE_CLIENTS;
|
2016-02-12 10:29:25 -08:00
|
|
|
loop {
|
|
|
|
|
tries += 1;
|
|
|
|
|
name = format!(r"\\.\pipe\__rust_anonymous_pipe1__.{}.{}",
|
|
|
|
|
c::GetCurrentProcessId(),
|
2017-11-01 12:32:13 -07:00
|
|
|
random_number());
|
2016-02-12 10:29:25 -08:00
|
|
|
let wide_name = OsStr::new(&name)
|
|
|
|
|
.encode_wide()
|
|
|
|
|
.chain(Some(0))
|
|
|
|
|
.collect::<Vec<_>>();
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
let mut flags = c::FILE_FLAG_FIRST_PIPE_INSTANCE |
|
|
|
|
|
c::FILE_FLAG_OVERLAPPED;
|
|
|
|
|
if ours_readable {
|
|
|
|
|
flags |= c::PIPE_ACCESS_INBOUND;
|
|
|
|
|
} else {
|
|
|
|
|
flags |= c::PIPE_ACCESS_OUTBOUND;
|
|
|
|
|
}
|
2016-02-12 10:29:25 -08:00
|
|
|
|
|
|
|
|
let handle = c::CreateNamedPipeW(wide_name.as_ptr(),
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
flags,
|
2016-02-12 10:29:25 -08:00
|
|
|
c::PIPE_TYPE_BYTE |
|
2016-11-10 02:00:25 +01:00
|
|
|
c::PIPE_READMODE_BYTE |
|
|
|
|
|
c::PIPE_WAIT |
|
|
|
|
|
reject_remote_clients_flag,
|
2016-02-12 10:29:25 -08:00
|
|
|
1,
|
|
|
|
|
4096,
|
|
|
|
|
4096,
|
|
|
|
|
0,
|
2016-06-24 20:54:52 +02:00
|
|
|
ptr::null_mut());
|
2016-02-12 10:29:25 -08:00
|
|
|
|
|
|
|
|
// We pass the FILE_FLAG_FIRST_PIPE_INSTANCE flag above, and we're
|
|
|
|
|
// also just doing a best effort at selecting a unique name. If
|
|
|
|
|
// ERROR_ACCESS_DENIED is returned then it could mean that we
|
|
|
|
|
// accidentally conflicted with an already existing pipe, so we try
|
|
|
|
|
// again.
|
|
|
|
|
//
|
|
|
|
|
// Don't try again too much though as this could also perhaps be a
|
|
|
|
|
// legit error.
|
2016-11-10 02:00:25 +01:00
|
|
|
// If ERROR_INVALID_PARAMETER is returned, this probably means we're
|
|
|
|
|
// running on pre-Vista version where PIPE_REJECT_REMOTE_CLIENTS is
|
|
|
|
|
// not supported, so we continue retrying without it. This implies
|
|
|
|
|
// reduced security on Windows versions older than Vista by allowing
|
|
|
|
|
// connections to this pipe from remote machines.
|
|
|
|
|
// Proper fix would increase the number of FFI imports and introduce
|
|
|
|
|
// significant amount of Windows XP specific code with no clean
|
|
|
|
|
// testing strategy
|
|
|
|
|
// for more info see https://github.com/rust-lang/rust/pull/37677
|
2016-02-12 10:29:25 -08:00
|
|
|
if handle == c::INVALID_HANDLE_VALUE {
|
|
|
|
|
let err = io::Error::last_os_error();
|
2016-11-10 02:00:25 +01:00
|
|
|
let raw_os_err = err.raw_os_error();
|
|
|
|
|
if tries < 10 {
|
|
|
|
|
if raw_os_err == Some(c::ERROR_ACCESS_DENIED as i32) {
|
|
|
|
|
continue
|
|
|
|
|
} else if reject_remote_clients_flag != 0 &&
|
|
|
|
|
raw_os_err == Some(c::ERROR_INVALID_PARAMETER as i32) {
|
|
|
|
|
reject_remote_clients_flag = 0;
|
|
|
|
|
tries -= 1;
|
|
|
|
|
continue
|
|
|
|
|
}
|
2016-02-12 10:29:25 -08:00
|
|
|
}
|
|
|
|
|
return Err(err)
|
|
|
|
|
}
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
ours = Handle::new(handle);
|
2016-02-12 10:29:25 -08:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
// Connect to the named pipe we just created. This handle is going to be
|
|
|
|
|
// returned in `theirs`, so if `ours` is readable we want this to be
|
|
|
|
|
// writable, otherwise if `ours` is writable we want this to be
|
|
|
|
|
// readable.
|
|
|
|
|
//
|
|
|
|
|
// Additionally we don't enable overlapped mode on this because most
|
|
|
|
|
// client processes aren't enabled to work with that.
|
2016-02-12 10:29:25 -08:00
|
|
|
let mut opts = OpenOptions::new();
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
opts.write(ours_readable);
|
|
|
|
|
opts.read(!ours_readable);
|
2016-02-12 10:29:25 -08:00
|
|
|
opts.share_mode(0);
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
let theirs = File::open(Path::new(&name), &opts)?;
|
|
|
|
|
let theirs = AnonPipe { inner: theirs.into_handle() };
|
2016-02-12 10:29:25 -08:00
|
|
|
|
std: Don't pass overlapped handles to processes
This commit fixes a mistake introduced in #31618 where overlapped handles were
leaked to child processes on Windows. On Windows once a handle is in overlapped
mode it should always have I/O executed with an instance of `OVERLAPPED`. Most
child processes, however, are not prepared to have their stdio handles in
overlapped mode as they don't use `OVERLAPPED` on reads/writes to the handle.
Now we haven't had any odd behavior in Rust up to this point, and the original
bug was introduced almost a year ago. I believe this is because it turns out
that if you *don't* pass an `OVERLAPPED` then the system will [supply one for
you][link]. In this case everything will go awry if you concurrently operate on
the handle. In Rust, however, the stdio handles are always locked, and there's
no way to not use them unlocked in libstd. Due to that change we've always had
synchronized access to these handles, which means that Rust programs typically
"just work".
Conversely, though, this commit fixes the test case included, which exhibits
behavior that other programs Rust spawns may attempt to execute. Namely, the
stdio handles may be concurrently used and having them in overlapped mode wreaks
havoc.
[link]: https://blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343
Closes #38811
2017-01-04 15:32:39 -08:00
|
|
|
Ok(Pipes {
|
|
|
|
|
ours: AnonPipe { inner: ours },
|
|
|
|
|
theirs: AnonPipe { inner: theirs.into_handle() },
|
|
|
|
|
})
|
2016-02-12 10:29:25 -08:00
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
2017-11-01 12:32:13 -07:00
|
|
|
fn random_number() -> usize {
|
|
|
|
|
static N: AtomicUsize = ATOMIC_USIZE_INIT;
|
|
|
|
|
loop {
|
|
|
|
|
if N.load(SeqCst) != 0 {
|
|
|
|
|
return N.fetch_add(1, SeqCst)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
N.store(hashmap_random_keys().0 as usize, SeqCst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
impl AnonPipe {
|
2015-04-14 11:17:47 -07:00
|
|
|
pub fn handle(&self) -> &Handle { &self.inner }
|
2015-07-15 23:31:24 -07:00
|
|
|
pub fn into_handle(self) -> Handle { self.inner }
|
2015-02-06 09:42:57 -08:00
|
|
|
|
|
|
|
|
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
2015-04-14 11:17:47 -07:00
|
|
|
self.inner.read(buf)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
2015-04-14 11:17:47 -07:00
|
|
|
self.inner.write(buf)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
}
|
2016-02-12 10:29:25 -08:00
|
|
|
|
|
|
|
|
pub fn read2(p1: AnonPipe,
|
|
|
|
|
v1: &mut Vec<u8>,
|
|
|
|
|
p2: AnonPipe,
|
|
|
|
|
v2: &mut Vec<u8>) -> io::Result<()> {
|
|
|
|
|
let p1 = p1.into_handle();
|
|
|
|
|
let p2 = p2.into_handle();
|
|
|
|
|
|
2016-03-22 22:01:37 -05:00
|
|
|
let mut p1 = AsyncPipe::new(p1, v1)?;
|
|
|
|
|
let mut p2 = AsyncPipe::new(p2, v2)?;
|
2016-02-12 10:29:25 -08:00
|
|
|
let objs = [p1.event.raw(), p2.event.raw()];
|
|
|
|
|
|
|
|
|
|
// In a loop we wait for either pipe's scheduled read operation to complete.
|
|
|
|
|
// If the operation completes with 0 bytes, that means EOF was reached, in
|
|
|
|
|
// which case we just finish out the other pipe entirely.
|
|
|
|
|
//
|
|
|
|
|
// Note that overlapped I/O is in general super unsafe because we have to
|
|
|
|
|
// be careful to ensure that all pointers in play are valid for the entire
|
|
|
|
|
// duration of the I/O operation (where tons of operations can also fail).
|
|
|
|
|
// The destructor for `AsyncPipe` ends up taking care of most of this.
|
|
|
|
|
loop {
|
|
|
|
|
let res = unsafe {
|
|
|
|
|
c::WaitForMultipleObjects(2, objs.as_ptr(), c::FALSE, c::INFINITE)
|
|
|
|
|
};
|
|
|
|
|
if res == c::WAIT_OBJECT_0 {
|
2016-03-22 22:01:37 -05:00
|
|
|
if !p1.result()? || !p1.schedule_read()? {
|
2016-02-12 10:29:25 -08:00
|
|
|
return p2.finish()
|
|
|
|
|
}
|
|
|
|
|
} else if res == c::WAIT_OBJECT_0 + 1 {
|
2016-03-22 22:01:37 -05:00
|
|
|
if !p2.result()? || !p2.schedule_read()? {
|
2016-02-12 10:29:25 -08:00
|
|
|
return p1.finish()
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return Err(io::Error::last_os_error())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct AsyncPipe<'a> {
|
|
|
|
|
pipe: Handle,
|
|
|
|
|
event: Handle,
|
|
|
|
|
overlapped: Box<c::OVERLAPPED>, // needs a stable address
|
|
|
|
|
dst: &'a mut Vec<u8>,
|
|
|
|
|
state: State,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
|
|
|
enum State {
|
|
|
|
|
NotReading,
|
|
|
|
|
Reading,
|
|
|
|
|
Read(usize),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> AsyncPipe<'a> {
|
|
|
|
|
fn new(pipe: Handle, dst: &'a mut Vec<u8>) -> io::Result<AsyncPipe<'a>> {
|
|
|
|
|
// Create an event which we'll use to coordinate our overlapped
|
|
|
|
|
// opreations, this event will be used in WaitForMultipleObjects
|
|
|
|
|
// and passed as part of the OVERLAPPED handle.
|
|
|
|
|
//
|
|
|
|
|
// Note that we do a somewhat clever thing here by flagging the
|
|
|
|
|
// event as being manually reset and setting it initially to the
|
|
|
|
|
// signaled state. This means that we'll naturally fall through the
|
|
|
|
|
// WaitForMultipleObjects call above for pipes created initially,
|
|
|
|
|
// and the only time an even will go back to "unset" will be once an
|
|
|
|
|
// I/O operation is successfully scheduled (what we want).
|
2016-03-22 22:01:37 -05:00
|
|
|
let event = Handle::new_event(true, true)?;
|
2016-02-12 10:29:25 -08:00
|
|
|
let mut overlapped: Box<c::OVERLAPPED> = unsafe {
|
|
|
|
|
Box::new(mem::zeroed())
|
|
|
|
|
};
|
|
|
|
|
overlapped.hEvent = event.raw();
|
|
|
|
|
Ok(AsyncPipe {
|
2017-08-06 22:54:09 -07:00
|
|
|
pipe,
|
|
|
|
|
overlapped,
|
|
|
|
|
event,
|
|
|
|
|
dst,
|
2016-02-12 10:29:25 -08:00
|
|
|
state: State::NotReading,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Executes an overlapped read operation.
|
|
|
|
|
///
|
|
|
|
|
/// Must not currently be reading, and returns whether the pipe is currently
|
|
|
|
|
/// at EOF or not. If the pipe is not at EOF then `result()` must be called
|
|
|
|
|
/// to complete the read later on (may block), but if the pipe is at EOF
|
|
|
|
|
/// then `result()` should not be called as it will just block forever.
|
|
|
|
|
fn schedule_read(&mut self) -> io::Result<bool> {
|
|
|
|
|
assert_eq!(self.state, State::NotReading);
|
|
|
|
|
let amt = unsafe {
|
|
|
|
|
let slice = slice_to_end(self.dst);
|
2016-03-22 22:01:37 -05:00
|
|
|
self.pipe.read_overlapped(slice, &mut *self.overlapped)?
|
2016-02-12 10:29:25 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// If this read finished immediately then our overlapped event will
|
|
|
|
|
// remain signaled (it was signaled coming in here) and we'll progress
|
|
|
|
|
// down to the method below.
|
|
|
|
|
//
|
|
|
|
|
// Otherwise the I/O operation is scheduled and the system set our event
|
|
|
|
|
// to not signaled, so we flag ourselves into the reading state and move
|
|
|
|
|
// on.
|
|
|
|
|
self.state = match amt {
|
|
|
|
|
Some(0) => return Ok(false),
|
|
|
|
|
Some(amt) => State::Read(amt),
|
|
|
|
|
None => State::Reading,
|
|
|
|
|
};
|
|
|
|
|
Ok(true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Wait for the result of the overlapped operation previously executed.
|
|
|
|
|
///
|
|
|
|
|
/// Takes a parameter `wait` which indicates if this pipe is currently being
|
|
|
|
|
/// read whether the function should block waiting for the read to complete.
|
|
|
|
|
///
|
|
|
|
|
/// Return values:
|
|
|
|
|
///
|
|
|
|
|
/// * `true` - finished any pending read and the pipe is not at EOF (keep
|
|
|
|
|
/// going)
|
|
|
|
|
/// * `false` - finished any pending read and pipe is at EOF (stop issuing
|
|
|
|
|
/// reads)
|
|
|
|
|
fn result(&mut self) -> io::Result<bool> {
|
|
|
|
|
let amt = match self.state {
|
|
|
|
|
State::NotReading => return Ok(true),
|
|
|
|
|
State::Reading => {
|
2016-03-22 22:01:37 -05:00
|
|
|
self.pipe.overlapped_result(&mut *self.overlapped, true)?
|
2016-02-12 10:29:25 -08:00
|
|
|
}
|
|
|
|
|
State::Read(amt) => amt,
|
|
|
|
|
};
|
|
|
|
|
self.state = State::NotReading;
|
|
|
|
|
unsafe {
|
|
|
|
|
let len = self.dst.len();
|
|
|
|
|
self.dst.set_len(len + amt);
|
|
|
|
|
}
|
|
|
|
|
Ok(amt != 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Finishes out reading this pipe entirely.
|
|
|
|
|
///
|
|
|
|
|
/// Waits for any pending and schedule read, and then calls `read_to_end`
|
|
|
|
|
/// if necessary to read all the remaining information.
|
|
|
|
|
fn finish(&mut self) -> io::Result<()> {
|
2016-03-22 22:01:37 -05:00
|
|
|
while self.result()? && self.schedule_read()? {
|
2016-02-12 10:29:25 -08:00
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Drop for AsyncPipe<'a> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
match self.state {
|
|
|
|
|
State::Reading => {}
|
|
|
|
|
_ => return,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we have a pending read operation, then we have to make sure that
|
|
|
|
|
// it's *done* before we actually drop this type. The kernel requires
|
|
|
|
|
// that the `OVERLAPPED` and buffer pointers are valid for the entire
|
|
|
|
|
// I/O operation.
|
|
|
|
|
//
|
|
|
|
|
// To do that, we call `CancelIo` to cancel any pending operation, and
|
|
|
|
|
// if that succeeds we wait for the overlapped result.
|
|
|
|
|
//
|
|
|
|
|
// If anything here fails, there's not really much we can do, so we leak
|
|
|
|
|
// the buffer/OVERLAPPED pointers to ensure we're at least memory safe.
|
|
|
|
|
if self.pipe.cancel_io().is_err() || self.result().is_err() {
|
|
|
|
|
let buf = mem::replace(self.dst, Vec::new());
|
|
|
|
|
let overlapped = Box::new(unsafe { mem::zeroed() });
|
|
|
|
|
let overlapped = mem::replace(&mut self.overlapped, overlapped);
|
|
|
|
|
mem::forget((buf, overlapped));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
|
|
|
|
|
if v.capacity() == 0 {
|
|
|
|
|
v.reserve(16);
|
|
|
|
|
}
|
|
|
|
|
if v.capacity() == v.len() {
|
|
|
|
|
v.reserve(1);
|
|
|
|
|
}
|
|
|
|
|
slice::from_raw_parts_mut(v.as_mut_ptr().offset(v.len() as isize),
|
|
|
|
|
v.capacity() - v.len())
|
|
|
|
|
}
|