Skip to content

Harden launchWhileAttentive against unhandled exceptions in block #4414

@guiyanakuang

Description

@guiyanakuang

Context

launchWhileAttentive (added in #4406) is the shared helper for gating background pollers on per-surface UI visibility. It collects an attention flow with collectLatest and calls block() immediately on resume plus on every interval tick while attention is true.

Problem

If block() throws, the collectLatest lambda terminates, the outer launch Job enters Cancelled, and the poller is permanently dead — subsequent attention transitions (true → false → true) cannot revive it. There is no signal to the user that polling has stopped.

The current sole consumer, DesktopNetworkProfileService.runDetection, wraps its body in runCatching, so the issue is latent. As soon as a second consumer (sync, cleanup, notifications) lands without that exact defensive boilerplate, the first transient failure silently kills its poller for the rest of the process lifetime.

Proposed fix

Move per-tick exception handling into launchWhileAttentive itself:

  • Catch Throwable around each block() invocation, log via KotlinLogging, continue to the next tick.
  • Rethrow CancellationException to keep structured concurrency semantics intact (so flow cancellation / parent scope cancellation still propagate).

The helper's name implies "polling gated by attention", and the intuitive contract is that a single bad tick should not stop future ticks. Consumers should only have to decide what to do; the scheduler owns the "what if it throws" question.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions