Skip to content

Commit

Permalink
Merge branch 'master' into port-fix-discard-all-message
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e authored Dec 8, 2024
2 parents bbff85d + c5ac785 commit 65e7ecf
Show file tree
Hide file tree
Showing 18 changed files with 140 additions and 93 deletions.
15 changes: 15 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,18 @@ updates:
commit-message:
prefix: ''
labels: []
groups:
cargo:
patterns:
- '*'
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
commit-message:
prefix: ''
labels: []
groups:
github-actions:
patterns:
- '*'
14 changes: 10 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ jobs:
matrix:
# aarch64/x86_64 macOS and aarch64 Linux are tested on Cirrus CI
include:
- rust: '1.61'
- rust: '1.63'
os: ubuntu-latest
- rust: '1.61'
- rust: '1.63'
os: windows-latest
- rust: stable
os: ubuntu-latest
Expand Down Expand Up @@ -135,7 +135,7 @@ jobs:
if ! git diff --exit-code; then
git add .
git commit -m "Update no_atomic.rs"
echo "::set-output name=success::false"
echo 'success=false' >>"${GITHUB_OUTPUT}"
fi
if: github.repository_owner == 'crossbeam-rs' && github.event_name == 'schedule'
- uses: peter-evans/create-pull-request@v5
Expand Down Expand Up @@ -174,14 +174,20 @@ jobs:

# Run miri.
miri:
strategy:
fail-fast: false
matrix:
group:
- channel
- others
runs-on: ubuntu-latest
timeout-minutes: 120 # TODO
steps:
- uses: taiki-e/checkout-action@v1
- name: Install Rust
run: rustup toolchain install nightly --component miri && rustup default nightly
- name: miri
run: ci/miri.sh
run: ci/miri.sh ${{ matrix.group }}

# Run cargo-careful.
careful:
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [
] }
unreachable_pub = "warn"
# unsafe_op_in_unsafe_fn = "warn" # Set at crate-level instead since https://github.com/rust-lang/rust/pull/100081 is not available on MSRV
[workspace.lints.clippy]
# Suppress buggy or noisy clippy lints
declare_interior_mutable_const = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/7665
lint_groups_priority = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12920
62 changes: 36 additions & 26 deletions ci/miri.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,46 @@ set -euxo pipefail
IFS=$'\n\t'
cd "$(dirname "$0")"/..

group=$1

# We need 'ts' for the per-line timing
sudo apt-get -y install moreutils
echo

export RUSTFLAGS="${RUSTFLAGS:-} -Z randomize-layout"
export RUSTDOCFLAGS="${RUSTDOCFLAGS:-} -Z randomize-layout"
export MIRIFLAGS="${MIRIFLAGS:-} -Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation"

MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation" \
MIRI_LEAK_CHECK='1' \
cargo miri test \
-p crossbeam-channel \
-p crossbeam-queue \
-p crossbeam-utils 2>&1 | ts -i '%.s '

# -Zmiri-ignore-leaks is needed because we use detached threads in tests in tests/golang.rs: https://github.com/rust-lang/miri/issues/1371
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-ignore-leaks" \
cargo miri test \
-p crossbeam-channel --test golang 2>&1 | ts -i '%.s '

# Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-tree-borrows" \
cargo miri test \
-p crossbeam-epoch \
-p crossbeam-skiplist \
-p crossbeam 2>&1 | ts -i '%.s '

# Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003
# -Zmiri-compare-exchange-weak-failure-rate=0.0 is needed because some sequential tests (e.g.,
# doctest of Stealer::steal) incorrectly assume that sequential weak CAS will never fail.
# -Zmiri-preemption-rate=0 is needed because this code technically has UB and Miri catches that.
MIRIFLAGS="-Zmiri-strict-provenance -Zmiri-symbolic-alignment-check -Zmiri-disable-isolation -Zmiri-tree-borrows -Zmiri-compare-exchange-weak-failure-rate=0.0 -Zmiri-preemption-rate=0" \
cargo miri test \
-p crossbeam-deque 2>&1 | ts -i '%.s '
case "${group}" in
channel)
MIRI_LEAK_CHECK='1' \
cargo miri test \
-p crossbeam-channel 2>&1 | ts -i '%.s '
# -Zmiri-ignore-leaks is needed because we use detached threads in tests in tests/golang.rs: https://github.com/rust-lang/miri/issues/1371
MIRIFLAGS="${MIRIFLAGS} -Zmiri-ignore-leaks" \
cargo miri test \
-p crossbeam-channel --test golang 2>&1 | ts -i '%.s '
;;
others)
cargo miri test \
-p crossbeam-queue \
-p crossbeam-utils 2>&1 | ts -i '%.s '
# Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003
MIRIFLAGS="${MIRIFLAGS} -Zmiri-tree-borrows" \
cargo miri test \
-p crossbeam-epoch \
-p crossbeam-skiplist \
-p crossbeam 2>&1 | ts -i '%.s '
# Use Tree Borrows instead of Stacked Borrows because epoch is not compatible with Stacked Borrows: https://github.com/crossbeam-rs/crossbeam/issues/545#issuecomment-1192785003
# -Zmiri-compare-exchange-weak-failure-rate=0.0 is needed because some sequential tests (e.g.,
# doctest of Stealer::steal) incorrectly assume that sequential weak CAS will never fail.
# -Zmiri-preemption-rate=0 is needed because this code technically has UB and Miri catches that.
MIRIFLAGS="${MIRIFLAGS} -Zmiri-tree-borrows -Zmiri-compare-exchange-weak-failure-rate=0.0 -Zmiri-preemption-rate=0" \
cargo miri test \
-p crossbeam-deque 2>&1 | ts -i '%.s '
;;
*)
echo "unknown crate group '${group}'"
exit 1
;;
esac
47 changes: 13 additions & 34 deletions ci/no_atomic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,24 @@ cd "$(dirname "$0")"/..
# Usage:
# ./ci/no_atomic.sh

file="no_atomic.rs"
file=no_atomic.rs

no_atomic=()
for target_spec in $(RUSTC_BOOTSTRAP=1 rustc +stable -Z unstable-options --print all-target-specs-json | jq -c '. | to_entries | .[]'); do
target=$(jq <<<"${target_spec}" -r '.key')
target_spec=$(jq <<<"${target_spec}" -c '.value')
res=$(jq <<<"${target_spec}" -r 'select(."atomic-cas" == false)')
[[ -z "${res}" ]] || no_atomic_cas+=("${target}")
max_atomic_width=$(jq <<<"${target_spec}" -r '."max-atomic-width"')
min_atomic_width=$(jq <<<"${target_spec}" -r '."min-atomic-width"')
case "${max_atomic_width}" in
# `"max-atomic-width" == 0` means that atomic is not supported at all.
# We do not have a cfg for targets with {8,16}-bit atomic only, so
# for now we treat them the same as targets that do not support atomic.
0) no_atomic+=("${target}") ;;
# It is not clear exactly what `"max-atomic-width" == null` means, but they
# actually seem to have the same max-atomic-width as the target-pointer-width.
# The targets currently included in this group are "mipsel-sony-psp",
# "thumbv4t-none-eabi", "thumbv6m-none-eabi", all of which are
# `"target-pointer-width" == "32"`, so assuming them `"max-atomic-width" == 32`
# for now.
null | 8 | 16 | 32 | 64 | 128) ;;
*) exit 1 ;;
esac
case "${min_atomic_width}" in
8 | null) ;;
*) no_atomic+=("${target}") ;;
esac
done
# `"max-atomic-width" == 0` means that atomic is not supported at all.
# We do not have a cfg for targets with {8,16}-bit atomic only, so
# for now we treat them the same as targets that do not support atomic.
# It is not clear exactly what `"max-atomic-width" == null` means, but they
# actually seem to have the same max-atomic-width as the target-pointer-width.
# The targets currently included in this group are "mipsel-sony-psp",
# "thumbv4t-none-eabi", "thumbv6m-none-eabi", all of which are
# `"target-pointer-width" == "32"`, so assuming them `"max-atomic-width" == 32`
# for now.
no_atomic=$(RUSTC_BOOTSTRAP=1 rustc +stable -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | select((.value."max-atomic-width" == 0) or (.value."min-atomic-width" and .value."min-atomic-width" != 8)) | " \"" + .key + "\","')

cat >"${file}" <<EOF
// This file is @generated by $(basename "$0").
// This file is @generated by ${0##*/}.
// It is not intended for manual editing.
const NO_ATOMIC: &[&str] = &[
EOF
for target in "${no_atomic[@]}"; do
echo " \"${target}\"," >>"${file}"
done
cat >>"${file}" <<EOF
${no_atomic}
];
EOF
33 changes: 22 additions & 11 deletions crossbeam-channel/src/flavors/list.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Unbounded channel implemented as a linked list.
use std::alloc::{alloc_zeroed, handle_alloc_error, Layout};
use std::boxed::Box;
use std::cell::UnsafeCell;
use std::marker::PhantomData;
Expand Down Expand Up @@ -50,11 +51,6 @@ struct Slot<T> {
}

impl<T> Slot<T> {
const UNINIT: Self = Self {
msg: UnsafeCell::new(MaybeUninit::uninit()),
state: AtomicUsize::new(0),
};

/// Waits until a message is written into the slot.
fn wait_write(&self) {
let backoff = Backoff::new();
Expand All @@ -77,11 +73,26 @@ struct Block<T> {

impl<T> Block<T> {
/// Creates an empty block.
fn new() -> Self {
Self {
next: AtomicPtr::new(ptr::null_mut()),
slots: [Slot::UNINIT; BLOCK_CAP],
fn new() -> Box<Self> {
let layout = Layout::new::<Self>();
assert!(
layout.size() != 0,
"Block should never be zero-sized, as it has an AtomicPtr field"
);
// SAFETY: layout is not zero-sized
let ptr = unsafe { alloc_zeroed(layout) };
// Handle allocation failure
if ptr.is_null() {
handle_alloc_error(layout)
}
// SAFETY: This is safe because:
// [1] `Block::next` (AtomicPtr) may be safely zero initialized.
// [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4].
// [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it
// holds a MaybeUninit.
// [4] `Slot::state` (AtomicUsize) may be safely zero initialized.
// TODO: unsafe { Box::new_zeroed().assume_init() }
unsafe { Box::from_raw(ptr.cast()) }
}

/// Waits until the next pointer is set.
Expand Down Expand Up @@ -223,13 +234,13 @@ impl<T> Channel<T> {
// If we're going to have to install the next block, allocate it in advance in order to
// make the wait for other threads as short as possible.
if offset + 1 == BLOCK_CAP && next_block.is_none() {
next_block = Some(Box::new(Block::<T>::new()));
next_block = Some(Block::<T>::new());
}

// If this is the first message to be sent into the channel, we need to allocate the
// first block and install it.
if block.is_null() {
let new = Box::into_raw(Box::new(Block::<T>::new()));
let new = Box::into_raw(Block::<T>::new());

if self
.tail
Expand Down
2 changes: 1 addition & 1 deletion crossbeam-channel/src/select_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ macro_rules! crossbeam_channel_internal {
const _LEN: usize = $crate::crossbeam_channel_internal!(@count ($($cases)*));
let _handle: &dyn $crate::internal::SelectHandle = &$crate::never::<()>();

#[allow(unused_mut)]
#[allow(unused_mut, clippy::zero_repeat_side_effects)]
let mut _sel = [(_handle, 0, ::std::ptr::null()); _LEN];

$crate::crossbeam_channel_internal!(
Expand Down
15 changes: 15 additions & 0 deletions crossbeam-channel/tests/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,3 +580,18 @@ fn channel_through_channel() {
})
.unwrap();
}

// If `Block` is created on the stack, the array of slots will multiply this `BigStruct` and
// probably overflow the thread stack. It's now directly created on the heap to avoid this.
#[test]
fn stack_overflow() {
const N: usize = 32_768;
struct BigStruct {
_data: [u8; N],
}

let (sender, receiver) = unbounded::<BigStruct>();
sender.send(BigStruct { _data: [0u8; N] }).unwrap();

for _data in receiver.try_iter() {}
}
1 change: 1 addition & 0 deletions crossbeam-channel/tests/select_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,7 @@ fn fairness1() {
assert!(hits.iter().all(|x| *x >= COUNT / hits.len() / 2));
}

#[cfg_attr(crossbeam_sanitize, ignore)] // TODO: flaky: https://github.com/crossbeam-rs/crossbeam/issues/1094
#[test]
fn fairness2() {
#[cfg(miri)]
Expand Down
3 changes: 1 addition & 2 deletions crossbeam-deque/src/deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::fmt;
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::ptr;
use std::slice;
use std::sync::atomic::{self, AtomicIsize, AtomicPtr, AtomicUsize, Ordering};
use std::sync::Arc;

Expand Down Expand Up @@ -52,7 +51,7 @@ impl<T> Buffer<T> {
/// Deallocates the buffer.
unsafe fn dealloc(self) {
drop(unsafe {
Box::from_raw(slice::from_raw_parts_mut(
Box::from_raw(ptr::slice_from_raw_parts_mut(
self.ptr.cast::<MaybeUninit<T>>(),
self.cap,
))
Expand Down
6 changes: 4 additions & 2 deletions crossbeam-queue/src/seg_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl<T> SegQueue<T> {
}
}

/// Pushes an element into the queue.
/// Pushes back an element to the tail.
///
/// # Examples
///
Expand Down Expand Up @@ -268,7 +268,7 @@ impl<T> SegQueue<T> {
}
}

/// Pops an element from the queue.
/// Pops the head element from the queue.
///
/// If the queue is empty, `None` is returned.
///
Expand All @@ -280,7 +280,9 @@ impl<T> SegQueue<T> {
/// let q = SegQueue::new();
///
/// q.push(10);
/// q.push(20);
/// assert_eq!(q.pop(), Some(10));
/// assert_eq!(q.pop(), Some(20));
/// assert!(q.pop().is_none());
/// ```
pub fn pop(&self) -> Option<T> {
Expand Down
4 changes: 2 additions & 2 deletions crossbeam-skiplist/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1456,12 +1456,12 @@ impl<'a, K: 'a, V: 'a> RefEntry<'a, K, V> {
}

/// Returns a reference to the key.
pub fn key(&self) -> &K {
pub fn key(&self) -> &'a K {
&self.node.key
}

/// Returns a reference to the value.
pub fn value(&self) -> &V {
pub fn value(&self) -> &'a V {
&self.node.value
}

Expand Down
6 changes: 3 additions & 3 deletions crossbeam-skiplist/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@
//! A solution to the above is to have the implementation wrap
//! each value in a lock. However, this has some repercussions:
//! * The map would no longer be lock-free, inhibiting scalability
//! and allowing for deadlocks.
//! and allowing for deadlocks.
//! * If a user of the map doesn't need mutable access, then they pay
//! the price of locks without actually needing them.
//! the price of locks without actually needing them.
//!
//! Instead, the approach taken by this crate gives more control to the user.
//! If mutable access is needed, then you can use interior mutability,
Expand Down Expand Up @@ -150,7 +150,7 @@
//! Crossbeam [does not currently provide a concurrent unordered map](https://github.com/crossbeam-rs/rfcs/issues/32).
//! That said, here are some other crates which may suit you:
//! * [`DashMap`](https://docs.rs/dashmap) implements a novel concurrent hash map
//! with good performance characteristics.
//! with good performance characteristics.
//! * [`flurry`](https://docs.rs/flurry) is a Rust port of Java's `ConcurrentHashMap`.
//!
//! [`insert`]: SkipMap::insert
Expand Down
4 changes: 2 additions & 2 deletions crossbeam-skiplist/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,12 +572,12 @@ impl<'a, K, V> Entry<'a, K, V> {
}

/// Returns a reference to the key.
pub fn key(&self) -> &K {
pub fn key(&self) -> &'a K {
self.inner.key()
}

/// Returns a reference to the value.
pub fn value(&self) -> &V {
pub fn value(&self) -> &'a V {
self.inner.value()
}

Expand Down
Loading

0 comments on commit 65e7ecf

Please sign in to comment.