@@ -39,6 +39,7 @@ const ARTWORK_BUDGET: usize = 8 * 1024 * 1024;
3939const PROPERTY_CALL_LIMIT : usize = 4 ;
4040const ACTION_CALL_LIMIT : usize = 4 ;
4141const ACTION_QUEUE_LIMIT : usize = 64 ;
42+ const MONITOR_RETRY_DELAY : Duration = Duration :: from_secs ( 5 ) ;
4243
4344#[ derive( Clone ) ]
4445struct Target {
@@ -52,6 +53,11 @@ struct Target {
5253 position_observed_at : Instant ,
5354}
5455
56+ struct DeferredTarget {
57+ owner : String ,
58+ retry_at : Option < Instant > ,
59+ }
60+
5561fn player_at ( target : & Target , observed_at : Instant ) -> MprisPlayer {
5662 let mut player = target. player . clone ( ) ;
5763 if player. playback_status == PlaybackStatus :: Playing {
@@ -77,7 +83,7 @@ pub(super) struct State {
7783 players : HashMap < u32 , Target > ,
7884 by_owner : HashMap < String , u32 > ,
7985 by_alias : HashMap < String , u32 > ,
80- deferred : HashMap < String , String > ,
86+ deferred : HashMap < String , DeferredTarget > ,
8187 active : Option < u32 > ,
8288 artwork_bytes : usize ,
8389 property_calls : Arc < Semaphore > ,
@@ -178,19 +184,42 @@ impl State {
178184 records
179185 }
180186
181- fn deferred_candidate ( & self , limit : usize ) -> Option < ( String , String ) > {
187+ fn remove_player_for_retry ( & mut self , player_id : u32 , retry_at : Instant ) -> Vec < MprisRecord > {
188+ let Some ( target) = self . players . get ( & player_id) else {
189+ return Vec :: new ( ) ;
190+ } ;
191+ let owner = target. owner . clone ( ) ;
192+ let aliases = target. aliases . iter ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
193+ let records = self . remove_player ( player_id) ;
194+ for alias in aliases {
195+ self . deferred . insert (
196+ alias,
197+ DeferredTarget {
198+ owner : owner. clone ( ) ,
199+ retry_at : Some ( retry_at) ,
200+ } ,
201+ ) ;
202+ }
203+ records
204+ }
205+
206+ fn deferred_candidate ( & self , limit : usize , now : Instant ) -> Option < ( String , String ) > {
207+ let ready = |target : & & DeferredTarget | target. retry_at . is_none_or ( |at| now >= at) ;
182208 self . deferred
183209 . iter ( )
184- . filter ( |( _, owner) | self . by_owner . contains_key ( owner. as_str ( ) ) )
210+ . filter ( |( _, target) | {
211+ ready ( target) && self . by_owner . contains_key ( target. owner . as_str ( ) )
212+ } )
185213 . min_by_key ( |( alias, _) | alias. as_str ( ) )
186- . map ( |( alias, owner ) | ( alias. clone ( ) , owner. clone ( ) ) )
214+ . map ( |( alias, target ) | ( alias. clone ( ) , target . owner . clone ( ) ) )
187215 . or_else ( || {
188216 ( self . players . len ( ) < limit)
189217 . then ( || {
190218 self . deferred
191219 . iter ( )
220+ . filter ( |( _, target) | ready ( target) )
192221 . min_by_key ( |( alias, _) | alias. as_str ( ) )
193- . map ( |( alias, owner ) | ( alias. clone ( ) , owner. clone ( ) ) )
222+ . map ( |( alias, target ) | ( alias. clone ( ) , target . owner . clone ( ) ) )
194223 } )
195224 . flatten ( )
196225 } )
@@ -402,7 +431,13 @@ async fn register_alias(
402431 return ;
403432 }
404433 if state. players . len ( ) >= player_limit ( ) {
405- state. deferred . insert ( alias, owner) ;
434+ state. deferred . insert (
435+ alias,
436+ DeferredTarget {
437+ owner,
438+ retry_at : None ,
439+ } ,
440+ ) ;
406441 return ;
407442 }
408443 state. deferred . remove ( & alias) ;
@@ -463,7 +498,7 @@ async fn remove_alias(
463498 if state
464499 . deferred
465500 . get ( alias)
466- . is_some_and ( |value| value == owner)
501+ . is_some_and ( |value| value. owner == owner)
467502 {
468503 state. deferred . remove ( alias) ;
469504 }
@@ -476,7 +511,7 @@ async fn remove_alias(
476511async fn remove_owner ( state : & std:: sync:: Arc < Mutex < State > > , common : & Common , owner : & str ) {
477512 let records = {
478513 let mut state = state. lock ( ) . await ;
479- state. deferred . retain ( |_, value| value != owner) ;
514+ state. deferred . retain ( |_, value| value. owner != owner) ;
480515 state
481516 . by_owner
482517 . get ( owner)
@@ -493,15 +528,23 @@ async fn remove_monitored_player(
493528 player_id : u32 ,
494529 owner : & str ,
495530) {
496- let records = {
531+ let removed = {
497532 let mut state = state. lock ( ) . await ;
498533 if state. by_owner . get ( owner) == Some ( & player_id) {
499- state. remove_player ( player_id)
534+ Some ( state. remove_player_for_retry ( player_id, Instant :: now ( ) + MONITOR_RETRY_DELAY ) )
500535 } else {
501- Vec :: new ( )
536+ None
502537 }
503538 } ;
539+ let Some ( records) = removed else {
540+ return ;
541+ } ;
504542 send_records ( common, records) . await ;
543+ let capacity_changed = state. lock ( ) . await . capacity_changed . clone ( ) ;
544+ tokio:: spawn ( async move {
545+ tokio:: time:: sleep ( MONITOR_RETRY_DELAY ) . await ;
546+ capacity_changed. notify_one ( ) ;
547+ } ) ;
505548}
506549
507550async fn retry_deferred (
@@ -510,7 +553,10 @@ async fn retry_deferred(
510553 common : & Common ,
511554) {
512555 loop {
513- let candidate = state. lock ( ) . await . deferred_candidate ( player_limit ( ) ) ;
556+ let candidate = state
557+ . lock ( )
558+ . await
559+ . deferred_candidate ( player_limit ( ) , Instant :: now ( ) ) ;
514560 let Some ( ( alias, owner) ) = candidate else {
515561 return ;
516562 } ;
@@ -1514,18 +1560,46 @@ mod tests {
15141560 let mut state = State :: default ( ) ;
15151561 state. players . insert ( 1 , target ( 1 , now) ) ;
15161562 state. by_owner . insert ( ":1.1" . into ( ) , 1 ) ;
1517- state
1518- . deferred
1519- . insert ( "org.mpris.MediaPlayer2.deferred" . into ( ) , ":1.2" . into ( ) ) ;
1563+ state. deferred . insert (
1564+ "org.mpris.MediaPlayer2.deferred" . into ( ) ,
1565+ DeferredTarget {
1566+ owner : ":1.2" . into ( ) ,
1567+ retry_at : None ,
1568+ } ,
1569+ ) ;
15201570
1521- assert_eq ! ( state. deferred_candidate( 1 ) , None ) ;
1571+ assert_eq ! ( state. deferred_candidate( 1 , now ) , None ) ;
15221572 state. remove_player ( 1 ) ;
15231573 assert_eq ! (
1524- state. deferred_candidate( 1 ) ,
1574+ state. deferred_candidate( 1 , now ) ,
15251575 Some ( ( "org.mpris.MediaPlayer2.deferred" . into( ) , ":1.2" . into( ) ) )
15261576 ) ;
15271577 }
15281578
1579+ #[ test]
1580+ fn failed_monitor_retries_only_after_its_cooldown ( ) {
1581+ let now = Instant :: now ( ) ;
1582+ let retry_at = now + MONITOR_RETRY_DELAY ;
1583+ let alias = "org.mpris.MediaPlayer2.retry" . to_string ( ) ;
1584+ let mut state = State :: default ( ) ;
1585+ let mut player = target ( 1 , now) ;
1586+ player. aliases . insert ( alias. clone ( ) ) ;
1587+ state. players . insert ( 1 , player) ;
1588+ state. by_owner . insert ( ":1.1" . into ( ) , 1 ) ;
1589+ state. by_alias . insert ( alias. clone ( ) , 1 ) ;
1590+
1591+ let records = state. remove_player_for_retry ( 1 , retry_at) ;
1592+ assert ! ( matches!(
1593+ records. first( ) ,
1594+ Some ( MprisRecord :: Delete { player_id: 1 } )
1595+ ) ) ;
1596+ assert_eq ! ( state. deferred_candidate( 1 , now) , None ) ;
1597+ assert_eq ! (
1598+ state. deferred_candidate( 1 , retry_at) ,
1599+ Some ( ( alias, ":1.1" . into( ) ) )
1600+ ) ;
1601+ }
1602+
15291603 #[ tokio:: test( flavor = "multi_thread" , worker_threads = 4 ) ]
15301604 async fn property_calls_are_strictly_limited_to_four ( ) {
15311605 let limiter = Arc :: new ( Semaphore :: new ( PROPERTY_CALL_LIMIT ) ) ;
0 commit comments