mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-29 00:00:11 +02:00
Merge branch 'tokio-1.6.x' into merge-1.6.1
This commit is contained in:
@@ -1,3 +1,11 @@
|
|||||||
|
# 1.6.1 (May 28, 2021)
|
||||||
|
|
||||||
|
This release reverts [#3518] because it doesn't work on some kernels due to
|
||||||
|
a kernel bug. ([#3803])
|
||||||
|
|
||||||
|
[#3518]: https://github.com/tokio-rs/tokio/issues/3518
|
||||||
|
[#3803]: https://github.com/tokio-rs/tokio/issues/3803
|
||||||
|
|
||||||
# 1.6.0 (May 14, 2021)
|
# 1.6.0 (May 14, 2021)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
+4
-4
@@ -7,7 +7,7 @@ name = "tokio"
|
|||||||
# - README.md
|
# - README.md
|
||||||
# - Update CHANGELOG.md.
|
# - Update CHANGELOG.md.
|
||||||
# - Create "v1.0.x" git tag.
|
# - Create "v1.0.x" git tag.
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Tokio Contributors <[email protected]>"]
|
authors = ["Tokio Contributors <[email protected]>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -42,7 +42,7 @@ full = [
|
|||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
fs = ["libc"]
|
fs = []
|
||||||
io-util = ["memchr", "bytes"]
|
io-util = ["memchr", "bytes"]
|
||||||
# stdin, stdout, stderr
|
# stdin, stdout, stderr
|
||||||
io-std = []
|
io-std = []
|
||||||
@@ -103,11 +103,11 @@ parking_lot = { version = "0.11.0", optional = true }
|
|||||||
tracing = { version = "0.1.21", default-features = false, features = ["std"], optional = true } # Not in full
|
tracing = { version = "0.1.21", default-features = false, features = ["std"], optional = true } # Not in full
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = { version = "0.2.87", optional = true }
|
libc = { version = "0.2.42", optional = true }
|
||||||
signal-hook-registry = { version = "1.1.1", optional = true }
|
signal-hook-registry = { version = "1.1.1", optional = true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dev-dependencies]
|
[target.'cfg(unix)'.dev-dependencies]
|
||||||
libc = { version = "0.2.87" }
|
libc = { version = "0.2.42" }
|
||||||
nix = { version = "0.19.0" }
|
nix = { version = "0.19.0" }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.winapi]
|
[target.'cfg(windows)'.dependencies.winapi]
|
||||||
|
|||||||
+2
-189
@@ -491,18 +491,14 @@ impl AsyncRead for File {
|
|||||||
loop {
|
loop {
|
||||||
match inner.state {
|
match inner.state {
|
||||||
Idle(ref mut buf_cell) => {
|
Idle(ref mut buf_cell) => {
|
||||||
let buf = buf_cell.as_mut().unwrap();
|
let mut buf = buf_cell.take().unwrap();
|
||||||
|
|
||||||
if !buf.is_empty() {
|
if !buf.is_empty() {
|
||||||
buf.copy_to(dst);
|
buf.copy_to(dst);
|
||||||
|
*buf_cell = Some(buf);
|
||||||
return Ready(Ok(()));
|
return Ready(Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(x) = try_nonblocking_read(me.std.as_ref(), dst) {
|
|
||||||
return Ready(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = buf_cell.take().unwrap();
|
|
||||||
buf.ensure_capacity_for(dst);
|
buf.ensure_capacity_for(dst);
|
||||||
let std = me.std.clone();
|
let std = me.std.clone();
|
||||||
|
|
||||||
@@ -760,186 +756,3 @@ impl Inner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(test)))]
|
|
||||||
pub(crate) fn try_nonblocking_read(
|
|
||||||
file: &crate::fs::sys::File,
|
|
||||||
dst: &mut ReadBuf<'_>,
|
|
||||||
) -> Option<std::io::Result<()>> {
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
static NONBLOCKING_READ_SUPPORTED: AtomicBool = AtomicBool::new(true);
|
|
||||||
if !NONBLOCKING_READ_SUPPORTED.load(Ordering::Relaxed) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let out = preadv2::preadv2_safe(file, dst, -1, preadv2::RWF_NOWAIT);
|
|
||||||
if let Err(err) = &out {
|
|
||||||
match err.raw_os_error() {
|
|
||||||
Some(libc::ENOSYS) => {
|
|
||||||
NONBLOCKING_READ_SUPPORTED.store(false, Ordering::Relaxed);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(libc::ENOTSUP) | Some(libc::EAGAIN) => return None,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(not(target_os = "linux"), test))]
|
|
||||||
pub(crate) fn try_nonblocking_read(
|
|
||||||
_file: &crate::fs::sys::File,
|
|
||||||
_dst: &mut ReadBuf<'_>,
|
|
||||||
) -> Option<std::io::Result<()>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
mod preadv2 {
|
|
||||||
use libc::{c_int, c_long, c_void, iovec, off_t, ssize_t};
|
|
||||||
use std::os::unix::prelude::AsRawFd;
|
|
||||||
|
|
||||||
use crate::io::ReadBuf;
|
|
||||||
|
|
||||||
pub(crate) fn preadv2_safe(
|
|
||||||
file: &std::fs::File,
|
|
||||||
dst: &mut ReadBuf<'_>,
|
|
||||||
offset: off_t,
|
|
||||||
flags: c_int,
|
|
||||||
) -> std::io::Result<()> {
|
|
||||||
unsafe {
|
|
||||||
/* We have to defend against buffer overflows manually here. The slice API makes
|
|
||||||
* this fairly straightforward. */
|
|
||||||
let unfilled = dst.unfilled_mut();
|
|
||||||
let mut iov = iovec {
|
|
||||||
iov_base: unfilled.as_mut_ptr() as *mut c_void,
|
|
||||||
iov_len: unfilled.len(),
|
|
||||||
};
|
|
||||||
/* We take a File object rather than an fd as reading from a sensitive fd may confuse
|
|
||||||
* other unsafe code that assumes that only they have access to that fd. */
|
|
||||||
let bytes_read = preadv2(
|
|
||||||
file.as_raw_fd(),
|
|
||||||
&mut iov as *mut iovec as *const iovec,
|
|
||||||
1,
|
|
||||||
offset,
|
|
||||||
flags,
|
|
||||||
);
|
|
||||||
if bytes_read < 0 {
|
|
||||||
Err(std::io::Error::last_os_error())
|
|
||||||
} else {
|
|
||||||
/* preadv2 returns the number of bytes read, e.g. the number of bytes that have
|
|
||||||
* written into `unfilled`. So it's safe to assume that the data is now
|
|
||||||
* initialised */
|
|
||||||
dst.assume_init(bytes_read as usize);
|
|
||||||
dst.advance(bytes_read as usize);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_preadv2_safe() {
|
|
||||||
use std::io::{Seek, Write};
|
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
use tempfile::tempdir;
|
|
||||||
|
|
||||||
let tmp = tempdir().unwrap();
|
|
||||||
let filename = tmp.path().join("file");
|
|
||||||
const MESSAGE: &[u8] = b"Hello this is a test";
|
|
||||||
{
|
|
||||||
let mut f = std::fs::File::create(&filename).unwrap();
|
|
||||||
f.write_all(MESSAGE).unwrap();
|
|
||||||
}
|
|
||||||
let f = std::fs::File::open(&filename).unwrap();
|
|
||||||
|
|
||||||
let mut buf = [MaybeUninit::<u8>::new(0); 50];
|
|
||||||
let mut br = ReadBuf::uninit(&mut buf);
|
|
||||||
|
|
||||||
// Basic use:
|
|
||||||
preadv2_safe(&f, &mut br, 0, 0).unwrap();
|
|
||||||
assert_eq!(br.initialized().len(), MESSAGE.len());
|
|
||||||
assert_eq!(br.filled(), MESSAGE);
|
|
||||||
|
|
||||||
// Here we check that offset works, but also that appending to a non-empty buffer
|
|
||||||
// behaves correctly WRT initialisation.
|
|
||||||
preadv2_safe(&f, &mut br, 5, 0).unwrap();
|
|
||||||
assert_eq!(br.initialized().len(), MESSAGE.len() * 2 - 5);
|
|
||||||
assert_eq!(br.filled(), b"Hello this is a test this is a test".as_ref());
|
|
||||||
|
|
||||||
// offset of -1 means use the current cursor. This has not been advanced by the
|
|
||||||
// previous reads because we specified an offset there.
|
|
||||||
preadv2_safe(&f, &mut br, -1, 0).unwrap();
|
|
||||||
assert_eq!(br.remaining(), 0);
|
|
||||||
assert_eq!(
|
|
||||||
br.filled(),
|
|
||||||
b"Hello this is a test this is a testHello this is a".as_ref()
|
|
||||||
);
|
|
||||||
|
|
||||||
// but the offset should have been advanced by that read
|
|
||||||
br.clear();
|
|
||||||
preadv2_safe(&f, &mut br, -1, 0).unwrap();
|
|
||||||
assert_eq!(br.filled(), b" test");
|
|
||||||
|
|
||||||
// This should be in cache, so RWF_NOWAIT should work, but it not being in cache
|
|
||||||
// (EAGAIN) or not supported by the underlying filesystem (ENOTSUP) is fine too.
|
|
||||||
br.clear();
|
|
||||||
match preadv2_safe(&f, &mut br, 0, RWF_NOWAIT) {
|
|
||||||
Ok(()) => assert_eq!(br.filled(), MESSAGE),
|
|
||||||
Err(e) => assert!(matches!(
|
|
||||||
e.raw_os_error(),
|
|
||||||
Some(libc::ENOTSUP) | Some(libc::EAGAIN)
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test handling large offsets
|
|
||||||
{
|
|
||||||
// I hope the underlying filesystem supports sparse files
|
|
||||||
let mut w = std::fs::OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.open(&filename)
|
|
||||||
.unwrap();
|
|
||||||
w.set_len(0x1_0000_0000).unwrap();
|
|
||||||
w.seek(std::io::SeekFrom::Start(0x1_0000_0000)).unwrap();
|
|
||||||
w.write_all(b"This is a Large File").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
br.clear();
|
|
||||||
preadv2_safe(&f, &mut br, 0x1_0000_0008, 0).unwrap();
|
|
||||||
assert_eq!(br.filled(), b"a Large File");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pos_to_lohi(offset: off_t) -> (c_long, c_long) {
|
|
||||||
// 64-bit offset is split over high and low 32-bits on 32-bit architectures.
|
|
||||||
// 64-bit architectures still have high and low arguments, but only the low
|
|
||||||
// one is inspected. See pos_from_hilo in linux/fs/read_write.c.
|
|
||||||
const HALF_LONG_BITS: usize = core::mem::size_of::<c_long>() * 8 / 2;
|
|
||||||
(
|
|
||||||
offset as c_long,
|
|
||||||
// We want to shift this off_t value by size_of::<c_long>(). We can't do
|
|
||||||
// it in one shift because if they're both 64-bits we'd be doing u64 >> 64
|
|
||||||
// which is implementation defined. Instead do it in two halves:
|
|
||||||
((offset >> HALF_LONG_BITS) >> HALF_LONG_BITS) as c_long,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) const RWF_NOWAIT: c_int = 0x00000008;
|
|
||||||
unsafe fn preadv2(
|
|
||||||
fd: c_int,
|
|
||||||
iov: *const iovec,
|
|
||||||
iovcnt: c_int,
|
|
||||||
offset: off_t,
|
|
||||||
flags: c_int,
|
|
||||||
) -> ssize_t {
|
|
||||||
// Call via libc::syscall rather than libc::preadv2. preadv2 is only supported by glibc
|
|
||||||
// and only since v2.26. By using syscall we don't need to worry about compatiblity with
|
|
||||||
// old glibc versions and it will work on Android and musl too. The downside is that you
|
|
||||||
// can't use `LD_PRELOAD` tricks any more to intercept these calls.
|
|
||||||
let (lo, hi) = pos_to_lohi(offset);
|
|
||||||
libc::syscall(libc::SYS_preadv2, fd, iov, iovcnt, lo, hi, flags) as ssize_t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,27 +22,6 @@ async fn basic_read() {
|
|||||||
|
|
||||||
assert_eq!(n, HELLO.len());
|
assert_eq!(n, HELLO.len());
|
||||||
assert_eq!(&buf[..n], HELLO);
|
assert_eq!(&buf[..n], HELLO);
|
||||||
|
|
||||||
// Drop the data from the cache to stimulate uncached codepath on Linux (see preadv2 in
|
|
||||||
// file.rs)
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
use std::os::unix::io::AsRawFd;
|
|
||||||
nix::unistd::fsync(tempfile.as_raw_fd()).unwrap();
|
|
||||||
nix::fcntl::posix_fadvise(
|
|
||||||
tempfile.as_raw_fd(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
nix::fcntl::PosixFadviseAdvice::POSIX_FADV_DONTNEED,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file = File::open(tempfile.path()).await.unwrap();
|
|
||||||
let n = file.read(&mut buf).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(n, HELLO.len());
|
|
||||||
assert_eq!(&buf[..n], HELLO);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
Reference in New Issue
Block a user