@@ -148,7 +148,15 @@ async def on_dvmtemplate_domain_shutdown(self, _event, **_kwargs) -> None:
148148 """
149149 Refresh preloaded disposables on shutdown.
150150 """
151- await self .refresh_preload ()
151+ self .refresh_outdated_preload ()
152+
153+ @qubes .events .handler ("property-reset:*" , "property-set:*" )
154+ def on_dvmtemplate_property_changed (self , _event , name , ** _kwargs ) -> None :
155+ """
156+ Refresh preloaded disposables if property affects the disposable.
157+ """
158+ if name not in qubes .vm .dispvm .PRELOAD_OUTDATED_IGNORED_PROPERTIES :
159+ self .refresh_outdated_preload (delay = 30 )
152160
153161 @qubes .events .handler ("domain-feature-pre-set:preload-dispvm-delay" )
154162 def on_feature_pre_set_preload_dispvm_delay (
@@ -641,21 +649,32 @@ def can_preload(self) -> bool:
641649 return True
642650 return False
643651
644- async def refresh_preload (self ) -> None :
652+ def refresh_outdated_preload (
653+ self , skip_check : bool = False , delay : Union [int , float ] = 4
654+ ) -> None :
645655 """
646- Refresh disposables which have outdated volumes.
656+ Refresh disposables which have outdated volumes or properties.
657+
658+ :param bool skip_check: Skip check of outdated preloads, refresh all.
647659 """
648660 assert isinstance (self , qubes .vm .BaseVM )
649- outdated = []
650- for qube in self .dispvms :
651- if not qube .is_preload or not any (
652- vol .is_outdated () for vol in qube .volumes .values ()
653- ):
661+ outdated : list | Iterator = []
662+ dispvms : list | Iterator = []
663+ if skip_check :
664+ outdated = self .dispvms
665+ else :
666+ dispvms = self .dispvms
667+ for qube in dispvms :
668+ if not qube .is_preload :
654669 continue
655- outdated .append (qube )
670+ if qube .is_preload_outdated ():
671+ assert isinstance (outdated , list )
672+ outdated .append (qube )
673+
656674 if outdated :
657675 self .remove_preload_from_list (
658- [qube .name for qube in outdated ], reason = "of outdated volume(s)"
676+ [qube .name for qube in outdated ],
677+ reason = "of outdated volume(s) or property(ies)" ,
659678 )
660679 tasks = [self .app .domains [qube ].cleanup () for qube in outdated ]
661680 asyncio .ensure_future (asyncio .gather (* tasks ))
@@ -664,7 +683,7 @@ async def refresh_preload(self) -> None:
664683 self .fire_event_async (
665684 "domain-preload-dispvm-start" ,
666685 reason = "of outdated volume(s)" ,
667- delay = 4 ,
686+ delay = delay ,
668687 )
669688 )
670689
@@ -682,16 +701,20 @@ def request_preload(self) -> Optional["qubes.vm.dispvm.DispVM"]:
682701 dispvm = None
683702 for item in preload_dispvm :
684703 qube = self .app .domains [item ]
685- if any (vol .is_outdated () for vol in qube .volumes .values ()):
704+ if outdated_reason := qube .is_preload_outdated ():
705+ if "properties" in outdated_reason :
706+ discard_reason = "property(ies): " + ", " .join (
707+ map (str , outdated_reason ["properties" ])
708+ )
709+ else :
710+ discard_reason = "volume(s)"
686711 qube .log .warning (
687- "Requested preloaded qube but it is outdated, trying "
688- "another one if available"
712+ "Discarding preloaded disposable as it has has outdated %s" ,
713+ discard_reason ,
689714 )
690- # The gap is filled after the delay set by the
691- # 'domain-shutdown' of its ancestors. Not refilling now to
692- # deliver a disposable faster.
715+ # Not refilling now to deliver a disposable faster.
693716 self .remove_preload_from_list (
694- [qube .name ], reason = "of outdated volume(s)"
717+ [qube .name ], reason = "of outdated " + discard_reason
695718 )
696719 # Delay to not affect this run.
697720 asyncio .ensure_future (
@@ -706,6 +729,7 @@ def request_preload(self) -> Optional["qubes.vm.dispvm.DispVM"]:
706729 "Found only outdated preloaded qube(s), falling back to "
707730 "normal disposable"
708731 )
732+ self .fill_preload_gap ()
709733 return None
710734 dispvm .mark_preload_requested ()
711735 return dispvm
0 commit comments