1
1
//! Communicates with the control thread and ships audio samples to the hardware
2
2
3
+ use std:: any:: Any ;
3
4
use std:: cell:: Cell ;
4
5
use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
5
6
use std:: sync:: Arc ;
6
- use std:: time:: Instant ;
7
+ use std:: time:: { Duration , Instant } ;
7
8
8
9
use crossbeam_channel:: { Receiver , Sender } ;
9
10
use dasp_sample:: FromSample ;
@@ -31,6 +32,7 @@ pub(crate) struct RenderThread {
31
32
buffer_offset : Option < ( usize , AudioRenderQuantum ) > ,
32
33
load_value_sender : Option < Sender < AudioRenderCapacityLoad > > ,
33
34
event_sender : Option < Sender < EventDispatch > > ,
35
+ garbage_collector : llq:: Producer < Box < dyn Any + Send + ' static > > ,
34
36
}
35
37
36
38
// SAFETY:
@@ -53,6 +55,8 @@ impl RenderThread {
53
55
load_value_sender : Option < Sender < AudioRenderCapacityLoad > > ,
54
56
event_sender : Option < Sender < EventDispatch > > ,
55
57
) -> Self {
58
+ let ( gc_producer, gc_consumer) = llq:: Queue :: new ( ) . split ( ) ;
59
+ spawn_garbage_collector_thread ( gc_consumer) ;
56
60
Self {
57
61
graph : None ,
58
62
sample_rate,
@@ -62,6 +66,7 @@ impl RenderThread {
62
66
buffer_offset : None ,
63
67
load_value_sender,
64
68
event_sender,
69
+ garbage_collector : gc_producer,
65
70
}
66
71
}
67
72
@@ -124,7 +129,7 @@ impl RenderThread {
124
129
}
125
130
NodeMessage { id, mut msg } => {
126
131
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 ) ;
128
133
}
129
134
}
130
135
}
@@ -296,6 +301,40 @@ impl RenderThread {
296
301
297
302
impl Drop for RenderThread {
298
303
fn drop ( & mut self ) {
304
+ self . garbage_collector
305
+ . push ( llq:: Node :: new ( Box :: new ( TerminateGarbageCollectorThread ) ) ) ;
299
306
log:: info!( "Audio render thread has been dropped" ) ;
300
307
}
301
308
}
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