Skip to content

Commit 50f6a9f

Browse files
committed
Improve panic messages for NodeCollection - track caller now works
1 parent 165f770 commit 50f6a9f

File tree

3 files changed

+58
-50
lines changed

3 files changed

+58
-50
lines changed

src/render/graph.rs

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,8 @@ impl Graph {
193193
}
194194

195195
pub fn add_edge(&mut self, source: (AudioNodeId, usize), dest: (AudioNodeId, usize)) {
196-
self.nodes[source.0]
197-
.get_mut()
196+
self.nodes
197+
.get_unchecked_mut(source.0)
198198
.outgoing_edges
199199
.push(OutgoingEdge {
200200
self_index: source.1,
@@ -206,8 +206,8 @@ impl Graph {
206206
}
207207

208208
pub fn remove_edge(&mut self, source: AudioNodeId, dest: AudioNodeId) {
209-
self.nodes[source]
210-
.get_mut()
209+
self.nodes
210+
.get_unchecked_mut(source)
211211
.outgoing_edges
212212
.retain(|edge| edge.other_id != dest);
213213

@@ -216,7 +216,7 @@ impl Graph {
216216

217217
pub fn remove_edges_from(&mut self, source: AudioNodeId) {
218218
// Remove outgoing edges
219-
self.nodes[source].get_mut().outgoing_edges.clear();
219+
self.nodes.get_unchecked_mut(source).outgoing_edges.clear();
220220

221221
// Remove incoming edges - we need to traverse all nodes
222222
self.nodes.values_mut().for_each(|node| {
@@ -241,21 +241,27 @@ impl Graph {
241241
}
242242

243243
pub fn mark_cycle_breaker(&mut self, index: AudioNodeId) {
244-
self.nodes[index].get_mut().cycle_breaker = true;
244+
self.nodes.get_unchecked_mut(index).cycle_breaker = true;
245245
}
246246

247247
pub fn set_channel_count(&mut self, index: AudioNodeId, v: usize) {
248-
self.nodes[index].get_mut().channel_config.count = v;
248+
self.nodes.get_unchecked_mut(index).channel_config.count = v;
249249
}
250250
pub fn set_channel_count_mode(&mut self, index: AudioNodeId, v: ChannelCountMode) {
251-
self.nodes[index].get_mut().channel_config.count_mode = v;
251+
self.nodes
252+
.get_unchecked_mut(index)
253+
.channel_config
254+
.count_mode = v;
252255
}
253256
pub fn set_channel_interpretation(&mut self, index: AudioNodeId, v: ChannelInterpretation) {
254-
self.nodes[index].get_mut().channel_config.interpretation = v;
257+
self.nodes
258+
.get_unchecked_mut(index)
259+
.channel_config
260+
.interpretation = v;
255261
}
256262

257263
pub fn route_message(&mut self, index: AudioNodeId, msg: &mut dyn Any) {
258-
self.nodes[index].get_mut().processor.onmessage(msg);
264+
self.nodes.get_unchecked_mut(index).processor.onmessage(msg);
259265
}
260266

261267
/// Helper function for `order_nodes` - traverse node and outgoing edges
@@ -278,7 +284,7 @@ impl Graph {
278284
let cycle_breaker_node = marked_temp
279285
.iter()
280286
.skip(pos)
281-
.find(|&&node_id| self.nodes[node_id].borrow().cycle_breaker);
287+
.find(|&&node_id| self.nodes.get_unchecked(node_id).borrow().cycle_breaker);
282288

283289
match cycle_breaker_node {
284290
Some(&node_id) => {
@@ -307,7 +313,13 @@ impl Graph {
307313
marked_temp.push(node_id);
308314

309315
// Visit outgoing nodes, and call `visit` on them recursively
310-
for edge in self.nodes[node_id].borrow().outgoing_edges.iter() {
316+
for edge in self
317+
.nodes
318+
.get_unchecked(node_id)
319+
.borrow()
320+
.outgoing_edges
321+
.iter()
322+
{
311323
let cycle_breaker_applied = self.visit(
312324
edge.other_id,
313325
marked,
@@ -386,9 +398,11 @@ impl Graph {
386398

387399
if cycle_breaker_applied {
388400
// clear the outgoing edges of the nodes that have been recognized as cycle breaker
389-
let nodes = &mut self.nodes;
390401
cycle_breakers.iter().for_each(|node_id| {
391-
nodes[*node_id].get_mut().outgoing_edges.clear();
402+
self.nodes
403+
.get_unchecked_mut(*node_id)
404+
.outgoing_edges
405+
.clear();
392406
});
393407

394408
continue;
@@ -423,16 +437,13 @@ impl Graph {
423437
// keep track of end-of-lifecyle nodes
424438
let mut nodes_dropped = false;
425439

426-
// for borrow-checker reasons, move mutable borrow of nodes out of self
427-
let nodes = &mut self.nodes;
428-
429440
// process every node, in topological sorted order
430441
self.ordered.iter().for_each(|index| {
431442
// acquire a mutable borrow of the current processing node
432-
let mut node = nodes[*index].borrow_mut();
443+
let mut node = self.nodes.get_unchecked(*index).borrow_mut();
433444

434445
// let the current node process (catch any panics that may occur)
435-
let params = AudioParamValues::from(nodes);
446+
let params = AudioParamValues::from(&self.nodes);
436447
scope.node_id.set(*index);
437448
let (success, tail_time) = {
438449
// We are abusing AssertUnwindSafe here, we cannot guarantee it upholds.
@@ -456,7 +467,7 @@ impl Graph {
456467
// audio params are connected to the 'hidden' usize::MAX output, ignore them here
457468
.filter(|edge| edge.other_index != usize::MAX)
458469
.for_each(|edge| {
459-
let mut output_node = nodes[edge.other_id].borrow_mut();
470+
let mut output_node = self.nodes.get_unchecked(edge.other_id).borrow_mut();
460471
output_node.has_inputs_connected = true;
461472
let signal = &node.outputs[edge.self_index];
462473
let channel_config = &output_node.channel_config.clone();
@@ -482,7 +493,7 @@ impl Graph {
482493
// Check if we can decommission this node (end of life)
483494
if can_free {
484495
// Node is dropped, remove it from the node list
485-
let mut node = nodes.remove(*index).into_inner();
496+
let mut node = self.nodes.remove(*index).into_inner();
486497
self.reclaim_id_channel
487498
.push(node.reclaim_id.take().unwrap());
488499
drop(node);
@@ -492,7 +503,7 @@ impl Graph {
492503

493504
// Nodes are only dropped when they do not have incoming connections.
494505
// But they may have AudioParams feeding into them, these can de dropped too.
495-
nodes.retain(|id, node| {
506+
self.nodes.retain(|id, node| {
496507
let node = node.get_mut(); // unwrap the RefCell
497508

498509
// Check if this node was connected to the dropped node. In that case, it is
@@ -524,7 +535,7 @@ impl Graph {
524535
if nodes_dropped {
525536
let mut i = 0;
526537
while i < self.ordered.len() {
527-
if nodes.get(self.ordered[i]).is_none() {
538+
if !self.nodes.contains(self.ordered[i]) {
528539
self.ordered.remove(i);
529540
} else {
530541
i += 1;
@@ -533,7 +544,7 @@ impl Graph {
533544
}
534545

535546
// Return the output buffer of destination node
536-
&self.nodes[AudioNodeId(0)].get_mut().outputs[0]
547+
&self.nodes.get_unchecked_mut(AudioNodeId(0)).outputs[0]
537548
}
538549
}
539550

@@ -735,7 +746,10 @@ mod tests {
735746
// dropped and the AudioNodeId(3) should be reclaimed
736747
add_node(&mut graph, 2, node.clone());
737748
// Mark the node as 'detached from the control thread', so it is allowed to drop
738-
graph.nodes[AudioNodeId(2)].get_mut().control_handle_dropped = true;
749+
graph
750+
.nodes
751+
.get_unchecked_mut(AudioNodeId(2))
752+
.control_handle_dropped = true;
739753

740754
// Connect the regular node to the AudioDestinationNode
741755
add_edge(&mut graph, 2, 0);
@@ -777,7 +791,10 @@ mod tests {
777791
// dropped and the AudioNodeId(3) should be reclaimed
778792
add_node(&mut graph, 2, node.clone());
779793
// Mark the node as 'detached from the control thread', so it is allowed to drop
780-
graph.nodes[AudioNodeId(2)].get_mut().control_handle_dropped = true;
794+
graph
795+
.nodes
796+
.get_unchecked_mut(AudioNodeId(2))
797+
.control_handle_dropped = true;
781798

782799
// Connect the regular node to the AudioDestinationNode
783800
add_edge(&mut graph, 2, 0);
@@ -786,7 +803,10 @@ mod tests {
786803
let param = Box::new(TestNode { tail_time: true }); // audio params have tail time true
787804
add_node(&mut graph, 3, param);
788805
// Mark the node as 'detached from the control thread', so it is allowed to drop
789-
graph.nodes[AudioNodeId(3)].get_mut().control_handle_dropped = true;
806+
graph
807+
.nodes
808+
.get_unchecked_mut(AudioNodeId(3))
809+
.control_handle_dropped = true;
790810

791811
// Connect the audioparam to the regular node
792812
add_audioparam(&mut graph, 3, 2);
@@ -828,7 +848,10 @@ mod tests {
828848
add_node(&mut graph, 2, node);
829849

830850
// Mark the node as 'detached from the control thread', so it is allowed to drop
831-
graph.nodes[AudioNodeId(2)].get_mut().control_handle_dropped = true;
851+
graph
852+
.nodes
853+
.get_unchecked_mut(AudioNodeId(2))
854+
.control_handle_dropped = true;
832855

833856
// Render a single quantum
834857
let scope = AudioWorkletGlobalScope {

src/render/node_collection.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use crate::context::{AudioNodeId, DESTINATION_NODE_ID};
22
use crate::render::graph::Node;
33

44
use std::cell::RefCell;
5-
use std::ops::{Index, IndexMut};
65

76
#[derive(Debug)]
87
pub(crate) struct NodeCollection {
@@ -59,8 +58,8 @@ impl NodeCollection {
5958
}
6059

6160
#[inline(always)]
62-
pub fn get(&self, index: AudioNodeId) -> Option<&RefCell<Node>> {
63-
self.nodes[index.0 as usize].as_ref()
61+
pub fn contains(&self, index: AudioNodeId) -> bool {
62+
self.nodes[index.0 as usize].is_some()
6463
}
6564

6665
#[inline(always)]
@@ -81,31 +80,17 @@ impl NodeCollection {
8180
}
8281
})
8382
}
84-
}
85-
86-
impl Index<AudioNodeId> for NodeCollection {
87-
type Output = RefCell<Node>;
8883

8984
#[track_caller]
9085
#[inline(always)]
91-
fn index(&self, index: AudioNodeId) -> &Self::Output {
92-
self.nodes
93-
.get(index.0 as usize)
94-
.unwrap_or_else(|| panic!("Unexpected index {} for NodeCollection", index.0))
95-
.as_ref()
96-
.unwrap_or_else(|| panic!("Index {} for dropped Node in NodeCollection", index.0))
86+
pub fn get_unchecked(&self, index: AudioNodeId) -> &RefCell<Node> {
87+
self.nodes[index.0 as usize].as_ref().unwrap()
9788
}
98-
}
9989

100-
impl IndexMut<AudioNodeId> for NodeCollection {
10190
#[track_caller]
10291
#[inline(always)]
103-
fn index_mut(&mut self, index: AudioNodeId) -> &mut Self::Output {
104-
self.nodes
105-
.get_mut(index.0 as usize)
106-
.unwrap_or_else(|| panic!("Unexpected index {} for NodeCollection", index.0))
107-
.as_mut()
108-
.unwrap_or_else(|| panic!("Index {} for dropped Node in NodeCollection", index.0))
92+
pub fn get_unchecked_mut(&mut self, index: AudioNodeId) -> &mut Node {
93+
self.nodes[index.0 as usize].as_mut().unwrap().get_mut()
10994
}
11095
}
11196

src/render/processor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ impl<'a> AudioParamValues<'a> {
195195
/// provide a slice of length equal to the render quantum size (default: 128)
196196
#[allow(clippy::missing_panics_doc)]
197197
pub fn get(&self, index: &AudioParamId) -> impl Deref<Target = [f32]> + '_ {
198-
DerefAudioRenderQuantumChannel(self.nodes[index.into()].borrow())
198+
DerefAudioRenderQuantumChannel(self.nodes.get_unchecked(index.into()).borrow())
199199
}
200200

201201
pub(crate) fn listener_params(&self) -> [impl Deref<Target = [f32]> + '_; 9] {

0 commit comments

Comments
 (0)