@@ -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  AudioNodeIdProvider  { 
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  AudioNodeIdProvider  { 
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  get ( & 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,8 +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 , 
78+     /// provider for new AudioNodeIds  
79+      audio_node_id_provider :   AudioNodeIdProvider , 
5280    /// destination node's current channel count 
5381     destination_channel_config :  ChannelConfig , 
5482    /// message channel from control to render thread 
@@ -83,9 +111,8 @@ impl BaseAudioContext for ConcreteBaseAudioContext {
83111        & self , 
84112        f :  F , 
85113    )  -> T  { 
86-         // create unique identifier for this node 
87-         let  id = self . inner . node_id_inc . fetch_add ( 1 ,  Ordering :: SeqCst ) ; 
88-         let  id = AudioNodeId ( id) ; 
114+         // create a unique id for this node 
115+         let  id = self . inner . audio_node_id_provider . get ( ) ; 
89116        let  registration = AudioContextRegistration  { 
90117            id, 
91118            context :  self . clone ( ) , 
@@ -97,6 +124,7 @@ impl BaseAudioContext for ConcreteBaseAudioContext {
97124        // pass the renderer to the audio graph 
98125        let  message = ControlMessage :: RegisterNode  { 
99126            id, 
127+             reclaim_id :  llq:: Node :: new ( id) , 
100128            node :  render, 
101129            inputs :  node. number_of_inputs ( ) , 
102130            outputs :  node. number_of_outputs ( ) , 
@@ -126,19 +154,22 @@ impl ConcreteBaseAudioContext {
126154        render_channel :  Sender < ControlMessage > , 
127155        event_channel :  Option < ( Sender < EventDispatch > ,  Receiver < EventDispatch > ) > , 
128156        offline :  bool , 
157+         node_id_consumer :  llq:: Consumer < AudioNodeId > , 
129158    )  -> Self  { 
130159        let  event_loop = EventLoop :: new ( ) ; 
131160        let  ( event_send,  event_recv)  = match  event_channel { 
132161            None  => ( None ,  None ) , 
133162            Some ( ( send,  recv) )  => ( Some ( send) ,  Some ( recv) ) , 
134163        } ; 
135164
165+         let  audio_node_id_provider = AudioNodeIdProvider :: new ( node_id_consumer) ; 
166+ 
136167        let  base_inner = ConcreteBaseAudioContextInner  { 
137168            sample_rate, 
138169            max_channel_count, 
139170            render_channel :  RwLock :: new ( render_channel) , 
140171            queued_messages :  Mutex :: new ( Vec :: new ( ) ) , 
141-             node_id_inc :   AtomicU64 :: new ( 0 ) , 
172+             audio_node_id_provider , 
142173            destination_channel_config :  ChannelConfigOptions :: default ( ) . into ( ) , 
143174            frames_played, 
144175            queued_audio_listener_msgs :  Mutex :: new ( Vec :: new ( ) ) , 
@@ -202,7 +233,10 @@ impl ConcreteBaseAudioContext {
202233
203234        // Validate if the hardcoded node IDs line up 
204235        debug_assert_eq ! ( 
205-             base. inner. node_id_inc. load( Ordering :: Relaxed ) , 
236+             base. inner
237+                 . audio_node_id_provider
238+                 . id_inc
239+                 . load( Ordering :: Relaxed ) , 
206240            LISTENER_PARAM_IDS . end, 
207241        ) ; 
208242
@@ -420,3 +454,19 @@ impl ConcreteBaseAudioContext {
420454        self . inner . event_loop . clear_handler ( event) ; 
421455    } 
422456} 
457+ 
458+ #[ cfg( test) ]  
459+ mod  tests { 
460+     use  super :: * ; 
461+ 
462+     #[ test]  
463+     fn  test_provide_node_id ( )  { 
464+         let  ( mut  id_producer,  id_consumer)  = llq:: Queue :: new ( ) . split ( ) ; 
465+         let  provider = AudioNodeIdProvider :: new ( id_consumer) ; 
466+         assert_eq ! ( provider. get( ) . 0 ,  0 ) ;  // newly assigned 
467+         assert_eq ! ( provider. get( ) . 0 ,  1 ) ;  // newly assigned 
468+         id_producer. push ( llq:: Node :: new ( AudioNodeId ( 0 ) ) ) ; 
469+         assert_eq ! ( provider. get( ) . 0 ,  0 ) ;  // reused 
470+         assert_eq ! ( provider. get( ) . 0 ,  2 ) ;  // newly assigned 
471+     } 
472+ } 
0 commit comments