Skip to content

Commit ad24275

Browse files
committed
Ensure AudioContext close_sync blocks control thread until completed
1 parent 50e115f commit ad24275

File tree

4 files changed

+26
-12
lines changed

4 files changed

+26
-12
lines changed

src/context/online.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ impl AudioContext {
274274
} else {
275275
// Acquire the audio graph from the current render thread, shutting it down
276276
let (graph_send, graph_recv) = crossbeam_channel::bounded(1);
277-
let message = ControlMessage::Shutdown { sender: graph_send };
277+
let message = ControlMessage::CloseAndRecycle { sender: graph_send };
278278
ctrl_msg_send.send(message).unwrap();
279279
if original_state == AudioContextState::Suspended {
280280
// We must wake up the render thread to be able to handle the shutdown.
@@ -442,8 +442,23 @@ impl AudioContext {
442442
/// Will panic when this function is called multiple times
443443
#[allow(clippy::missing_const_for_fn, clippy::unused_self)]
444444
pub fn close_sync(&self) {
445+
// First, stop rendering via a control message
446+
let (sender, receiver) = crossbeam_channel::bounded(0);
447+
let notify = OneshotNotify::Sync(sender);
448+
let suspend_msg = ControlMessage::Close { notify };
449+
self.base.send_control_msg(suspend_msg);
450+
451+
// Wait for the render thread to have processed the suspend message.
452+
// The AudioContextState will be updated by the render thread.
453+
receiver.recv().ok();
454+
455+
// Then ask the audio host to close the stream
445456
self.backend_manager.lock().unwrap().close();
457+
458+
// Stop the AudioRenderCapacity collection thread
446459
self.render_capacity.stop();
460+
461+
// TODO stop the event loop <https://github.com/orottier/web-audio-api-rs/issues/421>
447462
}
448463

449464
/// Creates a [`MediaStreamAudioSourceNode`](node::MediaStreamAudioSourceNode) from a

src/message.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub(crate) enum ControlMessage {
4040
MarkCycleBreaker { id: AudioNodeId },
4141

4242
/// Shut down and recycle the audio graph
43-
Shutdown {
43+
CloseAndRecycle {
4444
sender: crossbeam_channel::Sender<Graph>,
4545
},
4646

@@ -53,6 +53,9 @@ pub(crate) enum ControlMessage {
5353
/// Resume audio processing after suspending
5454
Resume { notify: OneshotNotify },
5555

56+
/// Stop audio processing
57+
Close { notify: OneshotNotify },
58+
5659
/// Generic message to be handled by AudioProcessor
5760
NodeMessage {
5861
id: AudioNodeId,

src/render/thread.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ impl RenderThread {
156156
MarkCycleBreaker { id } => {
157157
self.graph.as_mut().unwrap().mark_cycle_breaker(id);
158158
}
159-
Shutdown { sender } => {
159+
CloseAndRecycle { sender } => {
160160
self.set_state(AudioContextState::Suspended);
161161
let _ = sender.send(self.graph.take().unwrap());
162162
self.receiver = None;
@@ -193,6 +193,11 @@ impl RenderThread {
193193
self.set_state(AudioContextState::Running);
194194
notify.send();
195195
}
196+
Close { notify } => {
197+
self.suspended = true;
198+
self.set_state(AudioContextState::Closed);
199+
notify.send();
200+
}
196201
}
197202
}
198203
}
@@ -445,12 +450,6 @@ impl RenderThread {
445450

446451
impl Drop for RenderThread {
447452
fn drop(&mut self) {
448-
// If the audio graph is still there, this is an actual shutdown.
449-
// When the graph is None, this means we are only changing the sink - not shutting down.
450-
if self.graph.is_some() {
451-
self.set_state(AudioContextState::Closed);
452-
}
453-
454453
if let Some(gc) = self.garbage_collector.as_mut() {
455454
gc.push(llq::Node::new(Box::new(TerminateGarbageCollectorThread)))
456455
}

tests/online.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,6 @@ fn test_none_sink_id() {
114114
assert_eq!(state_changes.load(Ordering::Relaxed), 3); // resumed
115115

116116
context.close_sync();
117-
118-
// give event thread some time to pick up events
119-
std::thread::sleep(std::time::Duration::from_millis(20));
120117
assert_eq!(context.state(), AudioContextState::Closed);
121118
assert!(sink_stable.load(Ordering::SeqCst));
122119
assert_eq!(state_changes.load(Ordering::Relaxed), 4); // closed

0 commit comments

Comments
 (0)