11use std:: {
22 collections:: { HashMap , HashSet } ,
3+ hash:: Hash ,
34 ops:: { Deref , DerefMut } ,
45 sync:: { Arc , Mutex } ,
56} ;
@@ -11,34 +12,27 @@ mod warnings {
1112pub use warnings:: Warning ;
1213
1314#[ cfg( feature = "tracing" ) ]
14- pub trait RadioChannel < T > :
15- ' static + PartialEq + Eq + Clone + std:: hash:: Hash + std:: fmt:: Debug + Ord
16- {
15+ pub trait RadioChannel < T > : ' static + PartialEq + Eq + Clone + Hash + std:: fmt:: Debug + Ord {
1716 fn derive_channel ( self , _radio : & T ) -> Vec < Self > {
1817 vec ! [ self ]
1918 }
2019}
2120
2221#[ cfg( not( feature = "tracing" ) ) ]
23- pub trait RadioChannel < T > : ' static + PartialEq + Eq + Clone {
22+ pub trait RadioChannel < T > : ' static + PartialEq + Eq + Clone + Hash {
2423 fn derive_channel ( self , _radio : & T ) -> Vec < Self > {
2524 vec ! [ self ]
2625 }
2726}
2827
29- pub struct RadioListener < Channel > {
30- pub ( crate ) channel : Channel ,
31- pub ( crate ) drop_signal : CopyValue < ( ) > ,
32- }
33-
3428/// Holds a global state and all its subscribers.
3529pub struct RadioStation < Value , Channel >
3630where
3731 Channel : RadioChannel < Value > ,
3832 Value : ' static ,
3933{
4034 value : Signal < Value > ,
41- listeners : Signal < HashMap < ReactiveContext , RadioListener < Channel > > > ,
35+ listeners : Signal < HashMap < Channel , Arc < Mutex < HashSet < ReactiveContext > > > > > ,
4236}
4337
4438impl < Value , Channel > Clone for RadioStation < Value , Channel >
@@ -63,54 +57,30 @@ where
6357 ) -> bool {
6458 let listeners = self . listeners . peek_unchecked ( ) ;
6559 listeners
66- . get ( reactive_context )
67- . map ( |listener| & listener . channel == channel )
60+ . get ( channel )
61+ . map ( |contexts| contexts . lock ( ) . unwrap ( ) . contains ( reactive_context ) )
6862 . unwrap_or_default ( )
6963 }
7064
7165 pub ( crate ) fn listen ( & self , channel : Channel , reactive_context : ReactiveContext ) {
7266 dioxus_lib:: prelude:: warnings:: signal_write_in_component_body:: allow ( || {
7367 let mut listeners = self . listeners . write_unchecked ( ) ;
74- listeners. insert (
75- reactive_context,
76- RadioListener {
77- channel,
78- drop_signal : CopyValue :: new_maybe_sync ( ( ) ) ,
79- } ,
80- ) ;
81- } ) ;
82- }
83-
84- pub ( crate ) fn unlisten ( & self , reactive_context : ReactiveContext ) {
85- dioxus_lib:: prelude:: warnings:: signal_write_in_component_body:: allow ( || {
86- let mut listeners = match self . listeners . try_write_unchecked ( ) {
87- Err ( generational_box:: BorrowMutError :: Dropped ( _) ) => {
88- // It's safe to skip this error as the RadioStation's signals could have been dropped before the caller of this function.
89- // For instance: If you closed the app, the RadioStation would be dropped along all it's signals, causing the inner components
90- // to still have dropped signals and thus causing this error if they were to call the signals on a custom destructor.
91- return ;
92- }
93- Err ( e) => panic ! ( "Unexpected error: {e}" ) ,
94- Ok ( v) => v,
95- } ;
96- listeners. remove ( & reactive_context) ;
68+ let listeners = listeners. entry ( channel) . or_default ( ) ;
69+ reactive_context. subscribe ( listeners. clone ( ) ) ;
9770 } ) ;
9871 }
9972
10073 pub ( crate ) fn notify_listeners ( & self , channel : & Channel ) {
101- let mut listeners = self . listeners . write_unchecked ( ) ;
74+ let listeners = self . listeners . write_unchecked ( ) ;
10275
10376 #[ cfg( feature = "tracing" ) ]
10477 tracing:: info!( "Notifying {channel:?}" ) ;
10578
106- // Remove dropped listeners
107- dioxus_lib:: prelude:: warnings:: copy_value_hoisted:: allow ( || {
108- listeners. retain ( |_, listener| listener. drop_signal . try_write ( ) . is_ok ( ) ) ;
109- } ) ;
110-
111- for ( reactive_context, listener) in listeners. iter ( ) {
112- if & listener. channel == channel {
113- reactive_context. mark_dirty ( ) ;
79+ for ( listener_channel, listeners) in listeners. iter ( ) {
80+ if listener_channel == channel {
81+ for reactive_context in listeners. lock ( ) . unwrap ( ) . iter ( ) {
82+ reactive_context. mark_dirty ( ) ;
83+ }
11484 }
11585 }
11686 }
@@ -137,27 +107,30 @@ where
137107 self . value . peek ( )
138108 }
139109
140- # [ cfg ( not ( feature = "tracing" ) ) ]
141- pub fn print_metrics ( & self ) { }
110+ pub fn cleanup ( & self ) {
111+ let mut listeners = self . listeners . write_unchecked ( ) ;
142112
143- #[ cfg( feature = "tracing" ) ]
144- pub fn print_metrics ( & self ) {
145- use itertools:: Itertools ;
146- use tracing:: { info, span, Level } ;
113+ // Clean up those channels with no reactive contexts
114+ listeners. retain ( |_, listeners| !listeners. lock ( ) . unwrap ( ) . is_empty ( ) ) ;
147115
148- let mut channels_subscribers = HashMap :: < & Channel , usize > :: new ( ) ;
116+ #[ cfg( feature = "tracing" ) ]
117+ {
118+ use itertools:: Itertools ;
119+ use tracing:: { info, span, Level } ;
149120
150- let listeners = self . listeners . peek ( ) ;
121+ let mut channels_subscribers = HashMap :: < & Channel , usize > :: new ( ) ;
151122
152- for sub in listeners. values ( ) {
153- * channels_subscribers. entry ( & sub. channel ) . or_default ( ) += 1 ;
154- }
123+ for ( channel, listeners) in listeners. iter ( ) {
124+ * channels_subscribers. entry ( & channel) . or_default ( ) =
125+ listeners. lock ( ) . unwrap ( ) . len ( ) ;
126+ }
155127
156- let span = span ! ( Level :: DEBUG , "Radio Station Metrics" ) ;
157- let _enter = span. enter ( ) ;
128+ let span = span ! ( Level :: DEBUG , "Radio Station Metrics" ) ;
129+ let _enter = span. enter ( ) ;
158130
159- for ( channel, count) in channels_subscribers. iter ( ) . sorted ( ) {
160- info ! ( " {count} subscribers for {channel:?}" )
131+ for ( channel, count) in channels_subscribers. iter ( ) . sorted ( ) {
132+ info ! ( " {count} subscribers for {channel:?}" )
133+ }
161134 }
162135 }
163136}
@@ -169,8 +142,6 @@ where
169142{
170143 pub ( crate ) channel : Channel ,
171144 station : RadioStation < Value , Channel > ,
172- reactive_context : ReactiveContext ,
173- pub ( crate ) subscribers : Arc < Mutex < HashSet < ReactiveContext > > > ,
174145}
175146
176147impl < Value , Channel > RadioAntenna < Value , Channel >
@@ -180,23 +151,8 @@ where
180151 pub ( crate ) fn new (
181152 channel : Channel ,
182153 station : RadioStation < Value , Channel > ,
183- reactive_context : ReactiveContext ,
184154 ) -> RadioAntenna < Value , Channel > {
185- RadioAntenna {
186- channel,
187- station,
188- reactive_context,
189- subscribers : Arc :: default ( ) ,
190- }
191- }
192- }
193-
194- impl < Value , Channel > Drop for RadioAntenna < Value , Channel >
195- where
196- Channel : RadioChannel < Value > ,
197- {
198- fn drop ( & mut self ) {
199- self . station . unlisten ( self . reactive_context )
155+ RadioAntenna { channel, station }
200156 }
201157}
202158
@@ -219,7 +175,7 @@ where
219175 self . antenna . peek ( ) . station . notify_listeners ( channel)
220176 }
221177 if !self . channels . is_empty ( ) {
222- self . antenna . peek ( ) . station . print_metrics ( ) ;
178+ self . antenna . peek ( ) . station . cleanup ( ) ;
223179 }
224180 }
225181}
@@ -284,7 +240,6 @@ where
284240 dioxus_lib:: prelude:: warnings:: signal_write_in_component_body:: allow ( || {
285241 if let Some ( rc) = ReactiveContext :: current ( ) {
286242 let antenna = & self . antenna . write_unchecked ( ) ;
287- rc. subscribe ( antenna. subscribers . clone ( ) ) ;
288243 let channel = antenna. channel . clone ( ) ;
289244 let is_listening = antenna. station . is_listening ( & channel, & rc) ;
290245
@@ -422,7 +377,7 @@ where
422377 for channel in channel. derive_channel ( & guard. value ) {
423378 self . antenna . peek ( ) . station . notify_listeners ( & channel)
424379 }
425- self . antenna . peek ( ) . station . print_metrics ( ) ;
380+ self . antenna . peek ( ) . station . cleanup ( ) ;
426381 }
427382 }
428383
@@ -480,11 +435,7 @@ where
480435 let station = use_context :: < RadioStation < Value , Channel > > ( ) ;
481436
482437 let mut radio = use_hook ( || {
483- let antenna = RadioAntenna :: new (
484- channel. clone ( ) ,
485- station,
486- ReactiveContext :: current ( ) . unwrap ( ) ,
487- ) ;
438+ let antenna = RadioAntenna :: new ( channel. clone ( ) , station) ;
488439 Radio :: new ( Signal :: new ( antenna) )
489440 } ) ;
490441
0 commit comments