Skip to content

Commit 273911b

Browse files
authored
Merge pull request #336 from uklotzde/render-gc-thread
Add garbage collector thread as sidecar of render thread
2 parents d8f0225 + 34fb11f commit 273911b

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dasp_sample = "0.11"
2323
float_eq = "1.0"
2424
hound = "3.5"
2525
hrtf = "0.8"
26+
llq = "0.1.1"
2627
log = "0.4"
2728
num-complex = "0.4"
2829
realfft = "3.0"

src/context/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl AudioContextRegistration {
108108
pub fn post_message<M: Any + Send + 'static>(&self, msg: M) {
109109
let wrapped = crate::message::ControlMessage::NodeMessage {
110110
id: self.id,
111-
msg: Box::new(msg),
111+
msg: llq::Node::new(Box::new(msg)),
112112
};
113113
let _ = self.context.send_control_msg(wrapped);
114114
}

src/message.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,6 @@ pub(crate) enum ControlMessage {
4949
/// Generic message to be handled by AudioProcessor
5050
NodeMessage {
5151
id: AudioNodeId,
52-
msg: Box<dyn Any + Send + 'static>,
52+
msg: llq::Node<Box<dyn Any + Send + 'static>>,
5353
},
5454
}

src/render/thread.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! Communicates with the control thread and ships audio samples to the hardware
22
3+
use std::any::Any;
34
use std::cell::Cell;
45
use std::sync::atomic::{AtomicU64, Ordering};
56
use std::sync::Arc;
6-
use std::time::Instant;
7+
use std::time::{Duration, Instant};
78

89
use crossbeam_channel::{Receiver, Sender};
910
use dasp_sample::FromSample;
@@ -31,6 +32,7 @@ pub(crate) struct RenderThread {
3132
buffer_offset: Option<(usize, AudioRenderQuantum)>,
3233
load_value_sender: Option<Sender<AudioRenderCapacityLoad>>,
3334
event_sender: Option<Sender<EventDispatch>>,
35+
garbage_collector: llq::Producer<Box<dyn Any + Send + 'static>>,
3436
}
3537

3638
// SAFETY:
@@ -53,6 +55,8 @@ impl RenderThread {
5355
load_value_sender: Option<Sender<AudioRenderCapacityLoad>>,
5456
event_sender: Option<Sender<EventDispatch>>,
5557
) -> Self {
58+
let (gc_producer, gc_consumer) = llq::Queue::new().split();
59+
spawn_garbage_collector_thread(gc_consumer);
5660
Self {
5761
graph: None,
5862
sample_rate,
@@ -62,6 +66,7 @@ impl RenderThread {
6266
buffer_offset: None,
6367
load_value_sender,
6468
event_sender,
69+
garbage_collector: gc_producer,
6570
}
6671
}
6772

@@ -124,7 +129,7 @@ impl RenderThread {
124129
}
125130
NodeMessage { id, mut msg } => {
126131
self.graph.as_mut().unwrap().route_message(id, msg.as_mut());
127-
// FIXME: Drop the remains of the handled message outside of the render thread.
132+
self.garbage_collector.push(msg);
128133
}
129134
}
130135
}
@@ -296,6 +301,40 @@ impl RenderThread {
296301

297302
impl Drop for RenderThread {
298303
fn drop(&mut self) {
304+
self.garbage_collector
305+
.push(llq::Node::new(Box::new(TerminateGarbageCollectorThread)));
299306
log::info!("Audio render thread has been dropped");
300307
}
301308
}
309+
310+
// Controls the polling frequency of the garbage collector thread.
311+
const GARBAGE_COLLECTOR_THREAD_TIMEOUT: Duration = Duration::from_millis(100);
312+
313+
// Poison pill that terminates the garbage collector thread.
314+
#[derive(Debug)]
315+
struct TerminateGarbageCollectorThread;
316+
317+
// Spawns a sidecar thread of the `RenderThread` for dropping resources.
318+
fn spawn_garbage_collector_thread(consumer: llq::Consumer<Box<dyn Any + Send + 'static>>) {
319+
let _join_handle = std::thread::spawn(move || run_garbage_collector_thread(consumer));
320+
}
321+
322+
fn run_garbage_collector_thread(mut consumer: llq::Consumer<Box<dyn Any + Send + 'static>>) {
323+
log::info!("Entering garbage collector thread");
324+
loop {
325+
if let Some(node) = consumer.pop() {
326+
if node
327+
.as_ref()
328+
.downcast_ref::<TerminateGarbageCollectorThread>()
329+
.is_some()
330+
{
331+
log::info!("Terminating garbage collector thread");
332+
break;
333+
}
334+
// Implicitly drop the received node.
335+
} else {
336+
std::thread::sleep(GARBAGE_COLLECTOR_THREAD_TIMEOUT);
337+
}
338+
}
339+
log::info!("Exiting garbage collector thread");
340+
}

0 commit comments

Comments
 (0)