@@ -291,6 +291,7 @@ def __init__(self, app, xml, *args, **kwargs) -> None:
291291 self .volume_config = copy .deepcopy (self .default_volume_config )
292292 template = kwargs .get ("template" , None )
293293 self .preload_complete = asyncio .Event ()
294+ self .preload_requested_event = asyncio .Event ()
294295
295296 if xml is None :
296297 assert template is not None
@@ -394,11 +395,13 @@ def preload_requested(self) -> bool:
394395 @preload_requested .setter
395396 def preload_requested (self , value ) -> None :
396397 self ._preload_requested = value
398+ self .preload_requested_event .set ()
397399 self .fire_event ("property-reset:is_preload" , name = "is_preload" )
398400
399401 @preload_requested .deleter
400402 def preload_requested (self ) -> None :
401403 del self ._preload_requested
404+ self .preload_requested_event .clear ()
402405 self .fire_event ("property-reset:is_preload" , name = "is_preload" )
403406
404407 @qubes .stateless_property
@@ -484,30 +487,49 @@ async def on_domain_started_dispvm(
484487 """
485488 if not self .is_preload :
486489 return
487- timeout = self .qrexec_timeout
488- # https://github.com/QubesOS/qubes-issues/issues/9964
489- path = "/run/qubes-rpc:/usr/local/etc/qubes-rpc:/etc/qubes-rpc"
490- rpcs = ["qubes.WaitForRunningSystem" ]
491- if self .features .check_with_template (
492- "supported-feature.late-gui-daemon" , False
493- ):
494- rpcs .append ("qubes.WaitForSession" )
495- try :
496- async with asyncio .TaskGroup () as task_group :
497- for rpc in rpcs :
498- service = '$(PATH="' + path + '" command -v ' + rpc + ")"
499- task_group .create_task (
490+ if not self .preload_requested :
491+ timeout = self .qrexec_timeout
492+ # https://github.com/QubesOS/qubes-issues/issues/9964
493+ path = "/run/qubes-rpc:/usr/local/etc/qubes-rpc:/etc/qubes-rpc"
494+ rpcs = ["qubes.WaitForRunningSystem" ]
495+ if self .features .check_with_template (
496+ "supported-feature.late-gui-daemon" , False
497+ ):
498+ rpcs .append ("qubes.WaitForSession" )
499+ start_tasks = []
500+ for rpc in rpcs :
501+ service = '$(PATH="' + path + '" command -v ' + rpc + ")"
502+ start_tasks .append (
503+ asyncio .create_task (
500504 self .wait_operational_preload (rpc , service , timeout )
501505 )
502- except ExceptionGroup as e :
503- # Show detailed exception in desktop notification.
504- wanted_ex_group , _ = e .split (qubes .exc .QubesException )
505- if wanted_ex_group :
506- messages = [
507- "\n " + str (exc ) for exc in wanted_ex_group .exceptions
508- ]
509- raise qubes .exc .QubesException ("\n " .join (messages ))
510- raise
506+ )
507+ break_task = asyncio .create_task (
508+ self .preload_requested_event .wait ()
509+ )
510+ tasks = [break_task ] + start_tasks
511+ try :
512+ async for earliest_task in asyncio .as_completed (tasks ):
513+ await earliest_task
514+ if earliest_task == break_task :
515+ for incomplete_task in [
516+ t for t in start_tasks if not t .done ()
517+ ]:
518+ incomplete_task .cancel ()
519+ else :
520+ if all (t .done () for t in start_tasks ):
521+ break_task .cancel ()
522+ except asyncio .CancelledError :
523+ pass
524+ except ExceptionGroup as e :
525+ # Show detailed exception in desktop notification.
526+ wanted_ex_group , _ = e .split (qubes .exc .QubesException )
527+ if wanted_ex_group :
528+ messages = [
529+ "\n " + str (exc ) for exc in wanted_ex_group .exceptions
530+ ]
531+ raise qubes .exc .QubesException ("\n " .join (messages ))
532+ raise
511533 if not self .preload_requested :
512534 await self .pause ()
513535 self .log .info ("Preloading finished" )
@@ -722,7 +744,7 @@ async def from_appvm(
722744 [dispvm .name ], reason = "qube was requested"
723745 )
724746 dispvm .preload_requested = True
725- timeout = int (dispvm . qrexec_timeout * 1.2 )
747+ timeout = getattr (dispvm , " qrexec_timeout" , 60 )
726748 try :
727749 if not dispvm .features .get (
728750 "preload-dispvm-completed" , False
0 commit comments