Skip to content

Commit 3c62d58

Browse files
committed
Skip startup tasks when preload is requested
Waiting for the startup tasks only makes sense when it will be lingering for some time paused, so the first request is very fast. In case a request is made while the qube has not finished preloading, skipping the startup tasks allow faster marking of preloaded disposable as used. For: QubesOS/qubes-issues#10230 For: QubesOS/qubes-issues#1512
1 parent b720452 commit 3c62d58

1 file changed

Lines changed: 45 additions & 23 deletions

File tree

qubes/vm/dispvm.py

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)