[英]Rust WebAssembly Custom Elements Memory Deallocation Error
My first Rust-produced WASM is producing the following error, I have no idea how to go about it debugging. 我的第一个Rust生成的WASM产生了以下错误,我不知道如何进行调试。
wasm-000650c2-23:340 Uncaught RuntimeError: memory access out of bounds
at dlmalloc::dlmalloc::Dlmalloc::free::h36961b6fbcc40c05 (wasm-function[23]:670)
at __rdl_dealloc (wasm-function[367]:8)
at __rust_dealloc (wasm-function[360]:7)
at alloc::alloc::dealloc::h90df92e1f727e726 (wasm-function[146]:100)
at <alloc::alloc::Global as core::alloc::Alloc>::dealloc::h7f22ab187c7f5835 (wasm-function[194]:84)
at <alloc::raw_vec::RawVec<T, A>>::dealloc_buffer::hdce29184552be976 (wasm-function[82]:231)
at <alloc::raw_vec::RawVec<T, A> as core::ops::drop::Drop>::drop::h3910dccc175e44e6 (wasm-function[269]:38)
at core::ptr::real_drop_in_place::hd26be2408c00ce9d (wasm-function[267]:38)
at core::ptr::real_drop_in_place::h6acb013dbd13c114 (wasm-function[241]:50)
at core::ptr::real_drop_in_place::hb270ba635548ab74 (wasm-function[69]:192)
The context: latest Chrome, Rust wasm-bindgen code called from a TypeScript custom element, operating upon a canvas in the shadow DOM. 上下文:最新的Chrome,Rust ism-bindgen代码,从TypeScript自定义元素调用,在shadow DOM中的画布上运行。 Data rendered to the canvas comes from an HTML5 AudioBuffer. 呈现给画布的数据来自HTML5 AudioBuffer。 All rust variables are locally scoped. 所有生锈变量都是局部范围的。
The web component works perfectly if only one instance appears in the document, but if I further instances, a stack trace is dumped as above. 如果文档中只显示一个实例,则Web组件可以正常工作,但如果我进一步实例,则会像上面那样转储堆栈跟踪。 The code runs without any other issue. 代码运行没有任何其他问题。
I know there are outstanding memory bugs in Chrome -- is this what they look like, or can an experienced rust/wasm developer tell me if this is unusual? 我知道Chrome中存在未解决的内存错误 - 这是它们的样子,还是经验丰富的生锈/开发人员告诉我这是不寻常的?
js-sys = "0.3.19"
wasm-bindgen = "0.2.42"
wee_alloc = { version = "0.4.2", optional = true }
[dependencies.web-sys]
version = "0.3.4"
The rust code is small, and just renders two channels of an AudioBuffer to a supplied HTMLCanvasElement: 生锈代码很小,只需将AudioBuffer的两个通道呈现给提供的HTMLCanvasElement:
#[wasm_bindgen]
pub fn render(
canvas: web_sys::HtmlCanvasElement,
audio_buffer: &web_sys::AudioBuffer,
stroke_style: &JsValue,
line_width: f64,
step_size: usize,
) {
// ...
let mut channel_data: [Vec<f32>; 2] = unsafe { std::mem::uninitialized() }; // !
for channel_number in 0..1 {
channel_data[channel_number] = audio_buffer
.get_channel_data(channel_number as u32)
.unwrap();
}
// ...
I've tried commenting out functionality, and if the code doesn't touch the canvas but does the above, I get the error. 我已经尝试评论功能,如果代码没有触及画布但是上面做了,我得到了错误。 Making the below change results in a simple 'out of wam memory' error. 进行以下更改会导致简单的“out of wam memory”错误。 The audio file is is 1,200 k. 音频文件是1,200 k。
let channel_data: [Vec<f32>; 2] = [
audio_buffer.get_channel_data(0).unwrap(),
audio_buffer.get_channel_data(1).unwrap()
];
EDIT : The latter out of memory
error, for the correct code above, really threw me, but it is actually a Chrome bug . 编辑 :后者out of memory
不足错误,对于上面的正确代码,真的把我扔了,但它实际上是一个Chrome错误 。
Your problem is that you create a chunk of uninitialized memory and don't initialize it properly: 您的问题是您创建了一块未初始化的内存,并且没有正确初始化它:
let mut channel_data: [Vec<f32>; 2] = unsafe { std::mem::uninitialized() };
for channel_number in 0..1 {
channel_data[channel_number] = audio_buffer
.get_channel_data(channel_number as u32) // no need for `as u32` here btw
.unwrap();
}
Range
s (aka a..b
) are exclusive in Rust. Range
s (又名a..b
)在Rust中是独占的。 This means that your loop does not iterate twice as you would suppose, but instead only once and you have one uninitialized Vec<f32>
which then will panic while dropping it.
这意味着你的循环不会像你想象的那样迭代两次,而是只有一次并且你有一个未初始化的Vec<f32>
然后在放弃它时会发生恐慌。
(Please see Matthieu M.'s answer for a proper explanation) (请参阅Matthieu M.的答案以获得正确的解释)
There are a few possibilities here. 这里有一些可能性。
0..2
使用适当的范围,例如0..2
0..=1
使用包含范围 0..=1
let mut channel_data: [Vec<f32>; 2] = Default::default()
This will properly initialize the two Vec
s. 这将正确初始化两个Vec
。 For a more complete overview on how to initialize an array, see What is the proper way to initialize a fixed length array? 有关如何初始化数组的更完整概述,请参阅初始化固定长度数组的正确方法是什么?
As a sidenote: avoid using unsafe
, especially if you're new to Rust. 作为旁注:避免使用unsafe
,特别是如果你是Rust的新手。
There are two issues here: 这里有两个问题:
0..1
iterates over [0]
(it's exclusive). 你的迭代是错误的, 0..1
遍历[0]
(它是独占的)。 Let's check them one at a time. 我们一次检查一下。
unsafe
. 不要使用unsafe
。 In general, you should strive to avoid unsafe
. 一般来说,你应该努力避免unsafe
。 There are very few reasons to use it, and many ways to use it incorrectly (such as here). 使用它的原因很少,并且有很多方法不正确地使用它(例如这里)。
The issue. 问题。
In this particular case: 在这种特殊情况下:
let mut channel_data: [Vec<f32>; 2] = unsafe { std::mem::uninitialized() };
for channel_number in /*...*/ {
channel_data[channel_number] = /*...*/;
}
There are two issues: 有两个问题:
std::mem::uninitialized
is deprecated because of safety reasons; 出于安全原因,不推荐使用std::mem::uninitialized
; it's a very bad idea to use it. 使用它是一个非常糟糕的主意。 Its replacement is MaybeUninitialized
. 它的替代品是MaybeUninitialized
。 There is no assignment operator in Rust, in order to perform an assignment the language will: Rust中没有赋值运算符,为了执行赋值,语言将:
Dropping raw memory which thinks it's a Vec
is Undefined Behavior; 丢弃认为它是Vec
原始内存是未定义的行为; in this case the likely effect is that some random pointer value is read and freed. 在这种情况下,可能的效果是读取并释放一些随机指针值。 This may crash, this may free an unrelated pointer, leading to a latter crash or memory corruption, it's BAD . 这可能会崩溃,这可能会释放一个无关的指针,导致后一次崩溃或内存损坏,这是不好的 。
The solution. 解决方案。
There is little reason to use unsafe
here: 没有理由在这里使用unsafe
:
Default
implementation of Vec
does not allocate memory. 如果您坚持两步初始化,则不执行默认初始化几乎没有性能优势,因为Vec
的Default
实现不分配内存。 In short: 简而言之:
auto create_channel = |channel_number: u32| {
audio_buffer
.get_channel_data(channel_number)
.unwrap()
};
let mut channel_data = [create_channel(0), create_channel(1)];
is simple, safe, and most efficient. 简单,安全,高效。
If you insist on two-step initialization, then use iterators rather than indexing to avoid off-by-one errors. 如果你坚持两步初始化,那么使用迭代器而不是索引来避免一个一个错误。
In your case: 在你的情况下:
let mut channel_data = [vec!(), vec!()];
for (channel_number, channel) = channel_data.iter_mut().enumerate() {
*channel = audio_buffer
.get_channel_data(channel_number as u32)
.unwrap();
}
There are many utility functions on Iterator
, in this particular case, enumerate
will wrap the item returned by iter_mut()
(a &mut Vec<f32>
) into a tuple (usize, &mut Vec<32>)
: Iterator
上有许多实用函数,在这种特殊情况下, enumerate
会将iter_mut()
返回的项(a &mut Vec<f32>
) (usize, &mut Vec<32>)
到元组中(usize, &mut Vec<32>)
:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.