mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
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:
commit
339f574809
20
RELEASES.md
20
RELEASES.md
@ -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)
|
||||
==========================
|
||||
|
||||
@ -14,7 +26,7 @@ Language
|
||||
- [Merge `fn` syntax + cleanup item parsing.][68728]
|
||||
- [`item` macro fragments can be interpolated into `trait`s, `impl`s, and `extern` blocks.][69366]
|
||||
For example, you may now write:
|
||||
```rust
|
||||
```rust
|
||||
macro_rules! mac_trait {
|
||||
($i:item) => {
|
||||
trait T { $i }
|
||||
@ -82,7 +94,7 @@ Misc
|
||||
- [Certain checks in the `const_err` lint were deemed unrelated to const
|
||||
evaluation][69185], and have been moved to the `unconditional_panic` and
|
||||
`arithmetic_overflow` lints.
|
||||
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
|
||||
@ -173,7 +185,7 @@ Language
|
||||
(e.g. `type Foo: Ord;`).
|
||||
- `...` (the C-variadic type) may occur syntactically directly as the type of
|
||||
any function parameter.
|
||||
|
||||
|
||||
These are still rejected *semantically*, so you will likely receive an error
|
||||
but these changes can be seen and parsed by procedural macros and
|
||||
conditional compilation.
|
||||
@ -465,7 +477,7 @@ Compatibility Notes
|
||||
- [Using `#[inline]` on function prototypes and consts now emits a warning under
|
||||
`unused_attribute` lint.][65294] Using `#[inline]` anywhere else inside traits
|
||||
or `extern` blocks now correctly emits a hard error.
|
||||
|
||||
|
||||
[65294]: https://github.com/rust-lang/rust/pull/65294/
|
||||
[66103]: https://github.com/rust-lang/rust/pull/66103/
|
||||
[65843]: https://github.com/rust-lang/rust/pull/65843/
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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> {
|
||||
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,
|
||||
}
|
||||
} else {
|
||||
RangeMut { front: None, back: None, _marker: PhantomData }
|
||||
},
|
||||
length: self.length,
|
||||
if let Some(root) = &mut self.root {
|
||||
let (f, b) = full_range_search(root.as_mut());
|
||||
|
||||
IterMut {
|
||||
range: RangeMut { front: Some(f), back: Some(b), _marker: PhantomData },
|
||||
length: self.length,
|
||||
}
|
||||
} else {
|
||||
IterMut { range: RangeMut { front: None, back: None, _marker: PhantomData }, length: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user