Skip to content

Commit 86d4ce1

Browse files
authored
Merge pull request #376 from orottier/feature/no-garbage-collector-offline
Don't run the off-thread garbage collector for OfflineAudioContext
2 parents 8f17ce9 + 3d721a0 commit 86d4ce1

File tree

5 files changed

+43
-57
lines changed

5 files changed

+43
-57
lines changed

src/context/offline.rs

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,9 @@ pub struct OfflineAudioContext {
1717
/// the size of the buffer in sample-frames
1818
length: usize,
1919
/// the rendering 'thread', fully controlled by the offline context
20-
renderer: SingleUseRenderThread,
20+
renderer: RenderThread,
2121
}
2222

23-
mod private {
24-
use super::*;
25-
26-
pub(crate) struct SingleUseRenderThread(RenderThread);
27-
28-
impl SingleUseRenderThread {
29-
pub fn new(rt: RenderThread) -> Self {
30-
Self(rt)
31-
}
32-
33-
pub fn render_audiobuffer(self, buffer_size: usize) -> AudioBuffer {
34-
self.0.render_audiobuffer_sync(buffer_size)
35-
}
36-
}
37-
38-
// SAFETY:
39-
// The RenderThread is not Sync since it contains `AudioRenderQuantum`s (which use Rc) and `dyn
40-
// AudioProcessor` which may not allow sharing between threads. However we mark the
41-
// SingleUseRenderThread as Sync because it can only run once (and thus on a single thread)
42-
// NB: the render thread should never hand out the contained `Rc` and `AudioProcessor`s
43-
unsafe impl Sync for SingleUseRenderThread {}
44-
}
45-
use private::SingleUseRenderThread;
46-
4723
impl BaseAudioContext for OfflineAudioContext {
4824
fn base(&self) -> &ConcreteBaseAudioContext {
4925
&self.base
@@ -81,8 +57,6 @@ impl OfflineAudioContext {
8157
number_of_channels,
8258
receiver,
8359
frames_played_clone,
84-
None,
85-
None,
8660
);
8761

8862
// first, setup the base audio context
@@ -98,7 +72,7 @@ impl OfflineAudioContext {
9872
Self {
9973
base,
10074
length,
101-
renderer: SingleUseRenderThread::new(renderer),
75+
renderer,
10276
}
10377
}
10478

@@ -107,7 +81,7 @@ impl OfflineAudioContext {
10781
/// This function will block the current thread and returns the rendered `AudioBuffer`
10882
/// synchronously. An async version is currently not implemented.
10983
pub fn start_rendering_sync(self) -> AudioBuffer {
110-
self.renderer.render_audiobuffer(self.length)
84+
self.renderer.render_audiobuffer_sync(self.length)
11185
}
11286

11387
/// get the length of rendering audio buffer

src/io/cpal.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,14 @@ impl AudioBackendManager for CpalBackend {
186186
// shared atomic to report output latency to the control thread
187187
let output_latency = Arc::new(AtomicF64::new(0.));
188188

189-
let renderer = RenderThread::new(
189+
let mut renderer = RenderThread::new(
190190
sample_rate,
191191
preferred_config.channels as usize,
192192
ctrl_msg_recv.clone(),
193193
Arc::clone(&frames_played),
194-
Some(load_value_send.clone()),
195-
Some(event_send.clone()),
196194
);
195+
renderer.set_event_channels(load_value_send.clone(), event_send.clone());
196+
renderer.spawn_garbage_collector_thread();
197197

198198
log::debug!(
199199
"Attempt output stream with preferred config: {:?}",
@@ -227,14 +227,14 @@ impl AudioBackendManager for CpalBackend {
227227
&supported_config
228228
);
229229

230-
let renderer = RenderThread::new(
230+
let mut renderer = RenderThread::new(
231231
sample_rate,
232232
supported_config.channels as usize,
233233
ctrl_msg_recv,
234234
frames_played,
235-
Some(load_value_send),
236-
Some(event_send),
237235
);
236+
renderer.set_event_channels(load_value_send, event_send);
237+
renderer.spawn_garbage_collector_thread();
238238

239239
let spawned = spawn_output_stream(
240240
&device,

src/io/cubeb.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,14 @@ impl AudioBackendManager for CubebBackend {
182182
_ => cubeb::ChannelLayout::UNDEFINED, // TODO, does this work?
183183
};
184184

185-
let renderer = RenderThread::new(
185+
let mut renderer = RenderThread::new(
186186
sample_rate,
187187
number_of_channels,
188188
ctrl_msg_recv,
189189
frames_played,
190-
Some(load_value_send),
191-
Some(event_send),
192190
);
191+
renderer.set_event_channels(load_value_send, event_send);
192+
renderer.spawn_garbage_collector_thread();
193193

194194
let params = cubeb::StreamParamsBuilder::new()
195195
.format(cubeb::SampleFormat::Float32NE) // use float (native endian)

src/io/none.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,10 @@ impl AudioBackendManager for NoneBackend {
7777
event_send,
7878
} = render_thread_init;
7979

80-
let render_thread = RenderThread::new(
81-
sample_rate,
82-
MAX_CHANNELS,
83-
ctrl_msg_recv,
84-
frames_played,
85-
Some(load_value_send),
86-
Some(event_send),
87-
);
80+
let mut render_thread =
81+
RenderThread::new(sample_rate, MAX_CHANNELS, ctrl_msg_recv, frames_played);
82+
render_thread.set_event_channels(load_value_send, event_send);
83+
render_thread.spawn_garbage_collector_thread();
8884

8985
// Use a bounded channel for real-time safety. A maximum of 32 control messages (resume,
9086
// suspend, ..) will be handled per render quantum. The control thread will block when the

src/render/thread.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub(crate) struct RenderThread {
3232
buffer_offset: Option<(usize, AudioRenderQuantum)>,
3333
load_value_sender: Option<Sender<AudioRenderCapacityLoad>>,
3434
event_sender: Option<Sender<EventDispatch>>,
35-
garbage_collector: llq::Producer<Box<dyn Any + Send>>,
35+
garbage_collector: Option<llq::Producer<Box<dyn Any + Send>>>,
3636
}
3737

3838
// SAFETY:
@@ -52,21 +52,34 @@ impl RenderThread {
5252
number_of_channels: usize,
5353
receiver: Receiver<ControlMessage>,
5454
frames_played: Arc<AtomicU64>,
55-
load_value_sender: Option<Sender<AudioRenderCapacityLoad>>,
56-
event_sender: Option<Sender<EventDispatch>>,
5755
) -> Self {
58-
let (gc_producer, gc_consumer) = llq::Queue::new().split();
59-
spawn_garbage_collector_thread(gc_consumer);
6056
Self {
6157
graph: None,
6258
sample_rate,
6359
number_of_channels,
6460
frames_played,
6561
receiver: Some(receiver),
6662
buffer_offset: None,
67-
load_value_sender,
68-
event_sender,
69-
garbage_collector: gc_producer,
63+
load_value_sender: None,
64+
event_sender: None,
65+
garbage_collector: None,
66+
}
67+
}
68+
69+
pub(crate) fn set_event_channels(
70+
&mut self,
71+
load_value_sender: Sender<AudioRenderCapacityLoad>,
72+
event_sender: Sender<EventDispatch>,
73+
) {
74+
self.load_value_sender = Some(load_value_sender);
75+
self.event_sender = Some(event_sender);
76+
}
77+
78+
pub(crate) fn spawn_garbage_collector_thread(&mut self) {
79+
if self.garbage_collector.is_none() {
80+
let (gc_producer, gc_consumer) = llq::Queue::new().split();
81+
spawn_garbage_collector_thread(gc_consumer);
82+
self.garbage_collector = Some(gc_producer);
7083
}
7184
}
7285

@@ -129,7 +142,9 @@ impl RenderThread {
129142
}
130143
NodeMessage { id, mut msg } => {
131144
self.graph.as_mut().unwrap().route_message(id, msg.as_mut());
132-
self.garbage_collector.push(msg);
145+
if let Some(gc) = self.garbage_collector.as_mut() {
146+
gc.push(msg)
147+
}
133148
}
134149
}
135150
}
@@ -312,8 +327,9 @@ impl RenderThread {
312327

313328
impl Drop for RenderThread {
314329
fn drop(&mut self) {
315-
self.garbage_collector
316-
.push(llq::Node::new(Box::new(TerminateGarbageCollectorThread)));
330+
if let Some(gc) = self.garbage_collector.as_mut() {
331+
gc.push(llq::Node::new(Box::new(TerminateGarbageCollectorThread)))
332+
}
317333
log::info!("Audio render thread has been dropped");
318334
}
319335
}

0 commit comments

Comments
 (0)