@@ -17,6 +17,34 @@ use crossbeam_channel::{Receiver, SendError, Sender};
1717use std:: sync:: atomic:: { AtomicU64 , AtomicU8 , Ordering } ;
1818use std:: sync:: { Arc , Mutex , RwLock , RwLockWriteGuard } ;
1919
20+ /// This struct assigns new [`AudioNodeId`]s for [`AudioNode`]s
21+ ///
22+ /// It reuses the ids of decommissioned nodes to prevent unbounded growth of the audio graphs node
23+ /// list (which is stored in a Vec indexed by the AudioNodeId).
24+ struct AudioNodeIdIssuer {
25+ /// incrementing id
26+ id_inc : AtomicU64 ,
27+ /// receiver for decommissioned AudioNodeIds, which can be reused
28+ id_consumer : Mutex < llq:: Consumer < AudioNodeId > > ,
29+ }
30+
31+ impl AudioNodeIdIssuer {
32+ fn new ( id_consumer : llq:: Consumer < AudioNodeId > ) -> Self {
33+ Self {
34+ id_inc : AtomicU64 :: new ( 0 ) ,
35+ id_consumer : Mutex :: new ( id_consumer) ,
36+ }
37+ }
38+
39+ fn issue ( & self ) -> AudioNodeId {
40+ if let Some ( available_id) = self . id_consumer . lock ( ) . unwrap ( ) . pop ( ) {
41+ llq:: Node :: into_inner ( available_id)
42+ } else {
43+ AudioNodeId ( self . id_inc . fetch_add ( 1 , Ordering :: Relaxed ) )
44+ }
45+ }
46+ }
47+
2048/// The struct that corresponds to the Javascript `BaseAudioContext` object.
2149///
2250/// This object is returned from the `base()` method on
@@ -47,10 +75,8 @@ struct ConcreteBaseAudioContextInner {
4775 sample_rate : f32 ,
4876 /// max number of speaker output channels
4977 max_channel_count : usize ,
50- /// incrementing id to assign to audio nodes
51- node_id_inc : AtomicU64 ,
52- /// receiver for decommissioned AudioNodeIds, which can be reused
53- node_id_consumer : Mutex < llq:: Consumer < AudioNodeId > > ,
78+ /// issuer for new AudioNodeIds
79+ audio_node_id_issuer : AudioNodeIdIssuer ,
5480 /// destination node's current channel count
5581 destination_channel_config : ChannelConfig ,
5682 /// message channel from control to render thread
@@ -85,13 +111,8 @@ impl BaseAudioContext for ConcreteBaseAudioContext {
85111 & self ,
86112 f : F ,
87113 ) -> T {
88- // create unique identifier for this node
89- let id = if let Some ( available_id) = self . inner . node_id_consumer . lock ( ) . unwrap ( ) . pop ( ) {
90- llq:: Node :: into_inner ( available_id)
91- } else {
92- AudioNodeId ( self . inner . node_id_inc . fetch_add ( 1 , Ordering :: Relaxed ) )
93- } ;
94-
114+ // create a unique id for this node
115+ let id = self . inner . audio_node_id_issuer . issue ( ) ;
95116 let registration = AudioContextRegistration {
96117 id,
97118 context : self . clone ( ) ,
@@ -141,13 +162,14 @@ impl ConcreteBaseAudioContext {
141162 Some ( ( send, recv) ) => ( Some ( send) , Some ( recv) ) ,
142163 } ;
143164
165+ let audio_node_id_issuer = AudioNodeIdIssuer :: new ( node_id_consumer) ;
166+
144167 let base_inner = ConcreteBaseAudioContextInner {
145168 sample_rate,
146169 max_channel_count,
147170 render_channel : RwLock :: new ( render_channel) ,
148171 queued_messages : Mutex :: new ( Vec :: new ( ) ) ,
149- node_id_inc : AtomicU64 :: new ( 0 ) ,
150- node_id_consumer : Mutex :: new ( node_id_consumer) ,
172+ audio_node_id_issuer,
151173 destination_channel_config : ChannelConfigOptions :: default ( ) . into ( ) ,
152174 frames_played,
153175 queued_audio_listener_msgs : Mutex :: new ( Vec :: new ( ) ) ,
@@ -211,7 +233,10 @@ impl ConcreteBaseAudioContext {
211233
212234 // Validate if the hardcoded node IDs line up
213235 debug_assert_eq ! (
214- base. inner. node_id_inc. load( Ordering :: Relaxed ) ,
236+ base. inner
237+ . audio_node_id_issuer
238+ . id_inc
239+ . load( Ordering :: Relaxed ) ,
215240 LISTENER_PARAM_IDS . end,
216241 ) ;
217242
@@ -429,3 +454,19 @@ impl ConcreteBaseAudioContext {
429454 self . inner . event_loop . clear_handler ( event) ;
430455 }
431456}
457+
458+ #[ cfg( test) ]
459+ mod tests {
460+ use super :: * ;
461+
462+ #[ test]
463+ fn test_issue_node_id ( ) {
464+ let ( mut id_producer, id_consumer) = llq:: Queue :: new ( ) . split ( ) ;
465+ let issuer = AudioNodeIdIssuer :: new ( id_consumer) ;
466+ assert_eq ! ( issuer. issue( ) . 0 , 0 ) ; // newly assigned
467+ assert_eq ! ( issuer. issue( ) . 0 , 1 ) ; // newly assigned
468+ id_producer. push ( llq:: Node :: new ( AudioNodeId ( 0 ) ) ) ;
469+ assert_eq ! ( issuer. issue( ) . 0 , 0 ) ; // reused
470+ assert_eq ! ( issuer. issue( ) . 0 , 2 ) ; // newly assigned
471+ }
472+ }
0 commit comments