Auto merge of #71949 - Dylan-DPC:rollup-0gg02wd, r=Dylan-DPC

Rollup of 6 pull requests

Successful merges:

 - #71510 (Btreemap iter intertwined)
 - #71727 (SipHasher with keys initialized to 0 should just use new())
 - #71889 (Explain our RwLock implementation)
 - #71905 (Add command aliases from Cargo to x.py commands)
 - #71914 (Backport 1.43.1 release notes to master)
 - #71921 (explain the types used in the open64 call)

Failed merges:

r? @ghost
This commit is contained in:
bors 2020-05-06 11:24:13 +00:00
commit 339f574809
9 changed files with 197 additions and 119 deletions

View File

@ -1,3 +1,15 @@
Version 1.43.1 (2020-05-07)
===========================
* [Updated openssl-src to 1.1.1g for CVE-2020-1967.][71430]
* [Fixed the stabilization of AVX-512 features.][71473]
* [Fixed `cargo package --list` not working with unpublished dependencies.][cargo/8151]
[71430]: https://github.com/rust-lang/rust/pull/71430
[71473]: https://github.com/rust-lang/rust/issues/71473
[cargo/8151]: https://github.com/rust-lang/cargo/issues/8151
Version 1.43.0 (2020-04-23)
==========================

View File

@ -106,18 +106,18 @@ impl Flags {
Usage: x.py <subcommand> [options] [<paths>...]
Subcommands:
build Compile either the compiler or libraries
check Compile either the compiler or libraries, using cargo check
build, b Compile either the compiler or libraries
check, c Compile either the compiler or libraries, using cargo check
clippy Run clippy (uses rustup/cargo-installed clippy binary)
fix Run cargo fix
fmt Run rustfmt
test Build and run some test suites
test, t Build and run some test suites
bench Build and run some benchmarks
doc Build documentation
clean Clean out build directories
dist Build distribution artifacts
install Install distribution artifacts
run Run tools contained in this repository
run, r Run tools contained in this repository
To learn more about a subcommand, run `./x.py <subcommand> -h`",
);
@ -184,17 +184,21 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
// there on out.
let subcommand = args.iter().find(|&s| {
(s == "build")
|| (s == "b")
|| (s == "check")
|| (s == "c")
|| (s == "clippy")
|| (s == "fix")
|| (s == "fmt")
|| (s == "test")
|| (s == "t")
|| (s == "bench")
|| (s == "doc")
|| (s == "clean")
|| (s == "dist")
|| (s == "install")
|| (s == "run")
|| (s == "r")
});
let subcommand = match subcommand {
Some(s) => s,
@ -210,7 +214,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
// Some subcommands get extra options
match subcommand.as_str() {
"test" => {
"test" | "t" => {
opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
opts.optmulti("", "test-args", "extra arguments", "ARGS");
opts.optmulti(
@ -285,7 +289,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
}
// Extra help text for some commands
match subcommand.as_str() {
"build" => {
"build" | "b" => {
subcommand_help.push_str(
"\n
Arguments:
@ -312,7 +316,7 @@ Arguments:
Once this is done, build/$ARCH/stage1 contains a usable compiler.",
);
}
"check" => {
"check" | "c" => {
subcommand_help.push_str(
"\n
Arguments:
@ -362,7 +366,7 @@ Arguments:
./x.py fmt --check",
);
}
"test" => {
"test" | "t" => {
subcommand_help.push_str(
"\n
Arguments:
@ -407,7 +411,7 @@ Arguments:
./x.py doc --stage 1",
);
}
"run" => {
"run" | "r" => {
subcommand_help.push_str(
"\n
Arguments:
@ -453,11 +457,11 @@ Arguments:
}
let cmd = match subcommand.as_str() {
"build" => Subcommand::Build { paths },
"check" => Subcommand::Check { paths },
"build" | "b" => Subcommand::Build { paths },
"check" | "c" => Subcommand::Check { paths },
"clippy" => Subcommand::Clippy { paths },
"fix" => Subcommand::Fix { paths },
"test" => Subcommand::Test {
"test" | "t" => Subcommand::Test {
paths,
bless: matches.opt_present("bless"),
compare_mode: matches.opt_str("compare-mode"),
@ -487,7 +491,7 @@ Arguments:
"fmt" => Subcommand::Format { check: matches.opt_present("check") },
"dist" => Subcommand::Dist { paths },
"install" => Subcommand::Install { paths },
"run" => {
"run" | "r" => {
if paths.is_empty() {
println!("\nrun requires at least a path!\n");
usage(1, &opts, &subcommand_help, &extra_help);

View File

@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use std::iter::Iterator;
use std::ops::Bound::{Excluded, Unbounded};
use std::ops::RangeBounds;
use std::vec::Vec;
use rand::{seq::SliceRandom, thread_rng, Rng};
@ -117,7 +117,7 @@ map_find_rand_bench! {find_rand_10_000, 10_000, BTreeMap}
map_find_seq_bench! {find_seq_100, 100, BTreeMap}
map_find_seq_bench! {find_seq_10_000, 10_000, BTreeMap}
fn bench_iter(b: &mut Bencher, size: i32) {
fn bench_iteration(b: &mut Bencher, size: i32) {
let mut map = BTreeMap::<i32, i32>::new();
let mut rng = thread_rng();
@ -133,21 +133,21 @@ fn bench_iter(b: &mut Bencher, size: i32) {
}
#[bench]
pub fn iter_20(b: &mut Bencher) {
bench_iter(b, 20);
pub fn iteration_20(b: &mut Bencher) {
bench_iteration(b, 20);
}
#[bench]
pub fn iter_1000(b: &mut Bencher) {
bench_iter(b, 1000);
pub fn iteration_1000(b: &mut Bencher) {
bench_iteration(b, 1000);
}
#[bench]
pub fn iter_100000(b: &mut Bencher) {
bench_iter(b, 100000);
pub fn iteration_100000(b: &mut Bencher) {
bench_iteration(b, 100000);
}
fn bench_iter_mut(b: &mut Bencher, size: i32) {
fn bench_iteration_mut(b: &mut Bencher, size: i32) {
let mut map = BTreeMap::<i32, i32>::new();
let mut rng = thread_rng();
@ -163,18 +163,18 @@ fn bench_iter_mut(b: &mut Bencher, size: i32) {
}
#[bench]
pub fn iter_mut_20(b: &mut Bencher) {
bench_iter_mut(b, 20);
pub fn iteration_mut_20(b: &mut Bencher) {
bench_iteration_mut(b, 20);
}
#[bench]
pub fn iter_mut_1000(b: &mut Bencher) {
bench_iter_mut(b, 1000);
pub fn iteration_mut_1000(b: &mut Bencher) {
bench_iteration_mut(b, 1000);
}
#[bench]
pub fn iter_mut_100000(b: &mut Bencher) {
bench_iter_mut(b, 100000);
pub fn iteration_mut_100000(b: &mut Bencher) {
bench_iteration_mut(b, 100000);
}
fn bench_first_and_last(b: &mut Bencher, size: i32) {
@ -202,57 +202,83 @@ pub fn first_and_last_10k(b: &mut Bencher) {
bench_first_and_last(b, 10_000);
}
#[bench]
pub fn range_excluded_excluded(b: &mut Bencher) {
let size = 144;
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
const BENCH_RANGE_SIZE: i32 = 145;
const BENCH_RANGE_COUNT: i32 = BENCH_RANGE_SIZE * (BENCH_RANGE_SIZE - 1) / 2;
fn bench_range<F, R>(b: &mut Bencher, f: F)
where
F: Fn(i32, i32) -> R,
R: RangeBounds<i32>,
{
let map: BTreeMap<_, _> = (0..BENCH_RANGE_SIZE).map(|i| (i, i)).collect();
b.iter(|| {
for first in 0..size {
for last in first + 1..size {
black_box(map.range((Excluded(first), Excluded(last))));
let mut c = 0;
for i in 0..BENCH_RANGE_SIZE {
for j in i + 1..BENCH_RANGE_SIZE {
black_box(map.range(f(i, j)));
c += 1;
}
}
debug_assert_eq!(c, BENCH_RANGE_COUNT);
});
}
#[bench]
pub fn range_excluded_unbounded(b: &mut Bencher) {
let size = 144;
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
b.iter(|| {
for first in 0..size {
black_box(map.range((Excluded(first), Unbounded)));
}
});
pub fn range_included_excluded(b: &mut Bencher) {
bench_range(b, |i, j| i..j);
}
#[bench]
pub fn range_included_included(b: &mut Bencher) {
let size = 144;
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
b.iter(|| {
for first in 0..size {
for last in first..size {
black_box(map.range(first..=last));
}
}
});
bench_range(b, |i, j| i..=j);
}
#[bench]
pub fn range_included_unbounded(b: &mut Bencher) {
let size = 144;
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
b.iter(|| {
for first in 0..size {
black_box(map.range(first..));
}
});
bench_range(b, |i, _| i..);
}
#[bench]
pub fn range_unbounded_unbounded(b: &mut Bencher) {
let size = 144;
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
b.iter(|| map.range(..));
bench_range(b, |_, _| ..);
}
fn bench_iter(b: &mut Bencher, repeats: i32, size: i32) {
let map: BTreeMap<_, _> = (0..size).map(|i| (i, i)).collect();
b.iter(|| {
for _ in 0..repeats {
black_box(map.iter());
}
});
}
/// Contrast range_unbounded_unbounded with `iter()`.
#[bench]
pub fn range_unbounded_vs_iter(b: &mut Bencher) {
bench_iter(b, BENCH_RANGE_COUNT, BENCH_RANGE_SIZE);
}
#[bench]
pub fn iter_0(b: &mut Bencher) {
bench_iter(b, 1_000, 0);
}
#[bench]
pub fn iter_1(b: &mut Bencher) {
bench_iter(b, 1_000, 1);
}
#[bench]
pub fn iter_100(b: &mut Bencher) {
bench_iter(b, 1_000, 100);
}
#[bench]
pub fn iter_10k(b: &mut Bencher) {
bench_iter(b, 1_000, 10_000);
}
#[bench]
pub fn iter_1m(b: &mut Bencher) {
bench_iter(b, 1_000, 1_000_000);
}

View File

@ -1540,16 +1540,10 @@ impl<K, V> IntoIterator for BTreeMap<K, V> {
fn into_iter(self) -> IntoIter<K, V> {
let mut me = ManuallyDrop::new(self);
if let Some(root) = me.root.as_mut() {
let root1 = unsafe { ptr::read(root).into_ref() };
let root2 = unsafe { ptr::read(root).into_ref() };
let len = me.length;
if let Some(root) = me.root.take() {
let (f, b) = full_range_search(root.into_ref());
IntoIter {
front: Some(root1.first_leaf_edge()),
back: Some(root2.last_leaf_edge()),
length: len,
}
IntoIter { front: Some(f), back: Some(b), length: me.length }
} else {
IntoIter { front: None, back: None, length: 0 }
}
@ -2037,6 +2031,7 @@ where
}
}
/// Finds the leaf edges delimiting a specified range in or underneath a node.
fn range_search<BorrowType, K, V, Q: ?Sized, R: RangeBounds<Q>>(
root: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
range: R,
@ -2122,6 +2117,33 @@ where
}
}
/// Equivalent to `range_search(k, v, ..)` without the `Ord` bound.
fn full_range_search<BorrowType, K, V>(
root: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
) -> (
Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>,
Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>,
) {
// We duplicate the root NodeRef here -- we will never access it in a way
// that overlaps references obtained from the root.
let mut min_node = unsafe { ptr::read(&root) };
let mut max_node = root;
loop {
let front = min_node.first_edge();
let back = max_node.last_edge();
match (front.force(), back.force()) {
(Leaf(f), Leaf(b)) => {
return (f, b);
}
(Internal(min_int), Internal(max_int)) => {
min_node = min_int.descend();
max_node = max_int.descend();
}
_ => unreachable!("BTreeMap has different depths"),
};
}
}
impl<K, V> BTreeMap<K, V> {
/// Gets an iterator over the entries of the map, sorted by key.
///
@ -2146,12 +2168,12 @@ impl<K, V> BTreeMap<K, V> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn iter(&self) -> Iter<'_, K, V> {
Iter {
range: Range {
front: self.root.as_ref().map(|r| r.as_ref().first_leaf_edge()),
back: self.root.as_ref().map(|r| r.as_ref().last_leaf_edge()),
},
length: self.length,
if let Some(root) = &self.root {
let (f, b) = full_range_search(root.as_ref());
Iter { range: Range { front: Some(f), back: Some(b) }, length: self.length }
} else {
Iter { range: Range { front: None, back: None }, length: 0 }
}
}
@ -2178,19 +2200,15 @@ impl<K, V> BTreeMap<K, V> {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
if let Some(root) = &mut self.root {
let (f, b) = full_range_search(root.as_mut());
IterMut {
range: if let Some(root) = &mut self.root {
let root1 = root.as_mut();
let root2 = unsafe { ptr::read(&root1) };
RangeMut {
front: Some(root1.first_leaf_edge()),
back: Some(root2.last_leaf_edge()),
_marker: PhantomData,
range: RangeMut { front: Some(f), back: Some(b), _marker: PhantomData },
length: self.length,
}
} else {
RangeMut { front: None, back: None, _marker: PhantomData }
},
length: self.length,
IterMut { range: RangeMut { front: None, back: None, _marker: PhantomData }, length: 0 }
}
}

View File

@ -703,6 +703,10 @@ impl File {
| opts.get_access_mode()?
| opts.get_creation_mode()?
| (opts.custom_flags as c_int & !libc::O_ACCMODE);
// The third argument of `open64` is documented to have type `mode_t`. On
// some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
// However, since this is a variadic function, C integer promotion rules mean that on
// the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
let fd = FileDesc::new(fd);

View File

@ -28,14 +28,20 @@ impl Mutex {
//
// A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
// a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you
// try to re-lock it from the same thread when you already hold a lock.
// try to re-lock it from the same thread when you already hold a lock
// (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html).
// This is the case even if PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL
// (https://github.com/rust-lang/rust/issues/33770#issuecomment-220847521) -- in that
// case, `pthread_mutexattr_settype(PTHREAD_MUTEX_DEFAULT)` will of course be the same
// as setting it to `PTHREAD_MUTEX_NORMAL`, but not setting any mode will result in
// a Mutex where re-locking is UB.
//
// In practice, glibc takes advantage of this undefined behavior to
// implement hardware lock elision, which uses hardware transactional
// memory to avoid acquiring the lock. While a transaction is in
// progress, the lock appears to be unlocked. This isn't a problem for
// other threads since the transactional memory will abort if a conflict
// is detected, however no abort is generated if re-locking from the
// is detected, however no abort is generated when re-locking from the
// same thread.
//
// Since locking the same mutex twice will result in two aliasing &mut

View File

@ -22,32 +22,33 @@ impl RWLock {
pub unsafe fn read(&self) {
let r = libc::pthread_rwlock_rdlock(self.inner.get());
// According to the pthread_rwlock_rdlock spec, this function **may**
// fail with EDEADLK if a deadlock is detected. On the other hand
// pthread mutexes will *never* return EDEADLK if they are initialized
// as the "fast" kind (which ours always are). As a result, a deadlock
// situation may actually return from the call to pthread_rwlock_rdlock
// instead of blocking forever (as mutexes and Windows rwlocks do). Note
// that not all unix implementations, however, will return EDEADLK for
// their rwlocks.
// According to POSIX, when a thread tries to acquire this read lock
// while it already holds the write lock
// (or vice versa, or tries to acquire the write lock twice),
// "the call shall either deadlock or return [EDEADLK]"
// (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html,
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html).
// So, in principle, all we have to do here is check `r == 0` to be sure we properly
// got the lock.
//
// We roughly maintain the deadlocking behavior by panicking to ensure
// that this lock acquisition does not succeed.
//
// We also check whether this lock is already write locked. This
// is only possible if it was write locked by the current thread and
// the implementation allows recursive locking. The POSIX standard
// doesn't require recursively locking a rwlock to deadlock, but we can't
// allow that because it could lead to aliasing issues.
// However, (at least) glibc before version 2.25 does not conform to this spec,
// and can return `r == 0` even when this thread already holds the write lock.
// We thus check for this situation ourselves and panic when detecting that a thread
// got the write lock more than once, or got a read and a write lock.
if r == libc::EAGAIN {
panic!("rwlock maximum reader count exceeded");
} else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) {
// Above, we make sure to only access `write_locked` when `r == 0` to avoid
// data races.
if r == 0 {
// `pthread_rwlock_rdlock` succeeded when it should not have.
self.raw_unlock();
}
panic!("rwlock read lock would result in deadlock");
} else {
assert_eq!(r, 0);
// According to POSIX, for a properly initialized rwlock this can only
// return EAGAIN or EDEADLK or 0. We rely on that.
debug_assert_eq!(r, 0);
self.num_readers.fetch_add(1, Ordering::Relaxed);
}
}
@ -56,6 +57,7 @@ impl RWLock {
let r = libc::pthread_rwlock_tryrdlock(self.inner.get());
if r == 0 {
if *self.write_locked.get() {
// `pthread_rwlock_tryrdlock` succeeded when it should not have.
self.raw_unlock();
false
} else {
@ -69,17 +71,22 @@ impl RWLock {
#[inline]
pub unsafe fn write(&self) {
let r = libc::pthread_rwlock_wrlock(self.inner.get());
// See comments above for why we check for EDEADLK and write_locked. We
// also need to check that num_readers is 0.
// See comments above for why we check for EDEADLK and write_locked. For the same reason,
// we also need to check that there are no readers (tracked in `num_readers`).
if r == libc::EDEADLK
|| *self.write_locked.get()
|| (r == 0 && *self.write_locked.get())
|| self.num_readers.load(Ordering::Relaxed) != 0
{
// Above, we make sure to only access `write_locked` when `r == 0` to avoid
// data races.
if r == 0 {
// `pthread_rwlock_wrlock` succeeded when it should not have.
self.raw_unlock();
}
panic!("rwlock write lock would result in deadlock");
} else {
// According to POSIX, for a properly initialized rwlock this can only
// return EDEADLK or 0. We rely on that.
debug_assert_eq!(r, 0);
}
*self.write_locked.get() = true;
@ -89,6 +96,7 @@ impl RWLock {
let r = libc::pthread_rwlock_trywrlock(self.inner.get());
if r == 0 {
if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 {
// `pthread_rwlock_trywrlock` succeeded when it should not have.
self.raw_unlock();
false
} else {

View File

@ -24,7 +24,7 @@ struct Person {
enum E { A=1, B }
fn hash<T: Hash>(t: &T) -> u64 {
let mut s = SipHasher::new_with_keys(0, 0);
let mut s = SipHasher::new();
t.hash(&mut s);
s.finish()
}

View File

@ -7,9 +7,9 @@ use std::hash::{SipHasher, Hasher, Hash};
struct Empty;
pub fn main() {
let mut s1 = SipHasher::new_with_keys(0, 0);
let mut s1 = SipHasher::new();
Empty.hash(&mut s1);
let mut s2 = SipHasher::new_with_keys(0, 0);
let mut s2 = SipHasher::new();
Empty.hash(&mut s2);
assert_eq!(s1.finish(), s2.finish());
}