diff options
| author | Josh Stone <jistone@redhat.com> | 2024-11-07 09:51:52 -0800 |
|---|---|---|
| committer | Josh Stone <jistone@redhat.com> | 2024-11-07 10:09:45 -0800 |
| commit | 03383ad1021bd0ba34c28d7fda16e634a9ec8df4 (patch) | |
| tree | f87b1a10d1601ed6431008e08e3c80ffc49afaa0 | |
| parent | 3d1dba830a564d1118361345d7ada47a05241f45 (diff) | |
| download | rust-03383ad1021bd0ba34c28d7fda16e634a9ec8df4.tar.gz rust-03383ad1021bd0ba34c28d7fda16e634a9ec8df4.zip | |
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
| -rw-r--r-- | library/std/src/sync/mpmc/list.rs | 8 | ||||
| -rw-r--r-- | tests/ui/std/channel-stack-overflow-issue-102246.rs | 28 |
2 files changed, 32 insertions, 4 deletions
diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index 88a8c75f7c8..523e6d2f3bb 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -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 @@ 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 diff --git a/tests/ui/std/channel-stack-overflow-issue-102246.rs b/tests/ui/std/channel-stack-overflow-issue-102246.rs new file mode 100644 index 00000000000..52902fc563a --- /dev/null +++ b/tests/ui/std/channel-stack-overflow-issue-102246.rs @@ -0,0 +1,28 @@ +//@ run-pass +//@ 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() {} +} |
