Skip to content

Commit eb7473d

Browse files
committed
Handle pathological case where an AudioParam outlives an AudioNode
This could crash the render thread because previously it would drop the AudioParamRenderer alongside the AudioNode
1 parent 404c48b commit eb7473d

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

src/render/graph.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -451,23 +451,27 @@ impl Graph {
451451
// Nodes are only dropped when they do not have incoming connections.
452452
// But they may have AudioParams feeding into them, these can de dropped too.
453453
nodes.retain(|id, node| {
454+
let node = node.get_mut(); // unwrap the RefCell
455+
454456
// Check if this node was connected to the dropped node. In that case, it is
455457
// either an AudioParam (which can be dropped), or the AudioListener that feeds
456458
// into a PannerNode (which can be disconnected).
457459
let was_connected = {
458-
let outgoing_edges = &mut node.borrow_mut().outgoing_edges;
460+
let outgoing_edges = &mut node.outgoing_edges;
459461
let prev_len = outgoing_edges.len();
460462
outgoing_edges.retain(|e| e.other_id != *index);
461463
outgoing_edges.len() != prev_len
462464
};
463465

464-
// retain when special or not connected to this dropped node
465-
let special = id < 2; // never drop Listener and Destination node
466-
let retain = special || !was_connected;
466+
// Retain when
467+
// - special node (destination = id 0, listener = id 1), or
468+
// - not connected to this dropped node, or
469+
// - if the control thread still has a handle to it.
470+
let retain = id < 2 || !was_connected || !node.free_when_finished;
467471

468472
if !retain {
469473
self.reclaim_id_channel
470-
.push(node.get_mut().reclaim_id.take().unwrap());
474+
.push(node.reclaim_id.take().unwrap());
471475
}
472476
retain
473477
})
@@ -727,6 +731,8 @@ mod tests {
727731
// Add an AudioParam at id 4, it should be dropped alongside the regular node
728732
let param = Box::new(TestNode { tail_time: true }); // audio params have tail time true
729733
add_node(&mut graph, 3, param);
734+
// Mark the node as 'detached from the control thread', so it is allowed to drop
735+
graph.nodes[3].get_mut().free_when_finished = true;
730736

731737
// Connect the audioparam to the regular node
732738
add_audioparam(&mut graph, 3, 2);

tests/online.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,34 @@ fn test_panner_node_drop_panic() {
143143

144144
// A crashed thread will not fail the test (only if the main thread panics).
145145
// Instead inspect if there is progression of time in the audio context.
146+
let time = context.current_time();
147+
std::thread::sleep(std::time::Duration::from_millis(200));
148+
assert!(context.current_time() >= time + 0.15);
149+
}
146150

151+
#[test]
152+
fn test_audioparam_outlives_audionode() {
153+
let options = AudioContextOptions {
154+
sink_id: "none".into(),
155+
..AudioContextOptions::default()
156+
};
157+
let context = AudioContext::new(options);
158+
159+
// Create a node with an audioparam, drop to node but keep the audioparam
160+
let gain = context.create_gain();
161+
let gain_param = gain.gain().clone();
162+
drop(gain);
163+
164+
// Start the audio graph, and give some time to drop the gain node (it has no inputs connected
165+
// so dynamic lifetime will drop the node);
166+
std::thread::sleep(std::time::Duration::from_millis(200));
167+
168+
// We still have a handle to the param, so that should not be removed from the audio graph.
169+
// So by updating the value, the render thread should not crash.
170+
gain_param.set_value(1.);
171+
172+
// A crashed thread will not fail the test (only if the main thread panics).
173+
// Instead inspect if there is progression of time in the audio context.
147174
let time = context.current_time();
148175
std::thread::sleep(std::time::Duration::from_millis(200));
149176
assert!(context.current_time() >= time + 0.15);

0 commit comments

Comments
 (0)