Rollup merge of #132738 - cuviper:channel-heap-init, r=ibraheemdev
Initialize channel `Block`s directly on the heap The channel's `Block::new` was causing a stack overflow because it held 32 item slots, instantiated on the stack before moving to `Box::new`. The 32x multiplier made modestly-large item sizes untenable. That block is now initialized directly on the heap. Fixes #102246 try-job: test-various
This commit is contained in:
commit
4b904ceb46
@ -63,14 +63,14 @@ struct Block<T> {
|
||||
|
||||
impl<T> Block<T> {
|
||||
/// Creates an empty block.
|
||||
fn new() -> Block<T> {
|
||||
fn new() -> Box<Block<T>> {
|
||||
// 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.
|
||||
unsafe { MaybeUninit::zeroed().assume_init() }
|
||||
unsafe { Box::new_zeroed().assume_init() }
|
||||
}
|
||||
|
||||
/// Waits until the next pointer is set.
|
||||
@ -199,13 +199,13 @@ fn start_send(&self, token: &mut Token) -> bool {
|
||||
// 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
|
||||
|
29
tests/ui/std/channel-stack-overflow-issue-102246.rs
Normal file
29
tests/ui/std/channel-stack-overflow-issue-102246.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//@ run-pass
|
||||
//@ needs-threads
|
||||
//@ compile-flags: -Copt-level=0
|
||||
|
||||
// The channel's `Block::new` was causing a stack overflow because it held 32 item slots, which is
|
||||
// 1MiB for this test's `BigStruct` -- instantiated on the stack before moving to `Box::new`.
|
||||
//
|
||||
// That block is now initialized directly on the heap.
|
||||
//
|
||||
// Ref: https://github.com/rust-lang/rust/issues/102246
|
||||
|
||||
use std::sync::mpsc::channel;
|
||||
use std::thread;
|
||||
|
||||
const N: usize = 32_768;
|
||||
struct BigStruct {
|
||||
_data: [u8; N],
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (sender, receiver) = channel::<BigStruct>();
|
||||
|
||||
let thread1 = thread::spawn(move || {
|
||||
sender.send(BigStruct { _data: [0u8; N] }).unwrap();
|
||||
});
|
||||
|
||||
thread1.join().unwrap();
|
||||
for _data in receiver.try_iter() {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user