fix: session and lock-screen reliability (screencopy freeze, dropped lock requests, logind session lookup) - #3907
Conversation
|
Im excited for this! |
captureOutputBlocking() looped wl_display_roundtrip() with no deadline. When the compositor never delivers ready/failed for a frame (reproduced with lock-screen desktop snapshots on Hyprland — both outputs, every time), the loop spun forever inside LockScreen::lock() on the main loop: the whole shell froze, IPC went dark, and the session lock was never even requested. Bound the wait to 2 seconds, cancel the in-flight capture on timeout (the completion callback holds stack references), and let the caller fall back to the wallpaper background. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lock() returned true silently when isActive(), which made a stale m_locked flag (seen 2026-08-04 after an external locker + compositor lock-restore sequence) look like a successful lock while every request no-op'd. Log the state so the condition is diagnosable; a shell restart clears it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…up fails Both session lookups assumed the shell runs inside the login session's cgroup. Under the systemd user manager it does not: user@.service sits outside it, so GetSessionByPID answers NoSessionForPID, and XDG_SESSION_ID is not in that manager's environment either. The whole logind integration was therefore dark on this machine: the session lock monitor logged 'disabled: session path unavailable' (so loginctl lock-session never reached the lock screen), the idle-inhibit monitor never armed, and brightness fell back to the 'auto' session path — which resolves against the caller and so is the same dead end. Ask logind for this user's Display session as a last resort. Now logs 'logind session lock monitor active (/org/freedesktop/login1/session/_33)'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dbe94f6 to
940de7d
Compare
|
Split the PAM fork-deadlock commit out into #3908 — it closes #3848 and is easier to review on its own. This PR is now the remaining three fixes (screencopy timeout, dropped-lock-request logging, logind session fallback) and no longer touches |
|
Thanks for splitting these fixes out @nocstah . The lock-request logging change looks good, and the logind session-resolution change is a useful direction. I would request changes on the screencopy timeout before merging. The deadline is checked only after Could this use a deadline-aware wait on the Wayland file descriptor instead, then dispatch events while progress is available? Please also add coverage for the timeout and cancellation path, including that a late completion callback cannot access the stack references captured by For the logind fallback, please extract the duplicated session-resolution logic into logind-owned shared code. |
…undtrips
Review feedback on the first cut: the deadline was only checked *after*
wl_display_roundtrip() returned, so a connection that stops making progress
blocks inside the roundtrip and the timeout can never fire. Repeated blocking
roundtrips with sleep_for() also do not fit the poll-based event loop.
Wait on the Wayland fd instead: dispatch what is already queued, prepare_read,
flush, poll() for at most the remaining budget, then read and dispatch what
arrived. The wait is now bounded by the deadline rather than wrapped in it,
and there is no sleep.
The capture and wait sides move into screencopy_blocking.{h,cpp} behind small
injectable ops so the deadline arithmetic and the cancellation path are
testable without a compositor. The completion state also moves off the
caller's stack into a shared_ptr: giving up flags it abandoned, so a late
completion is a no-op instead of a write through references to `out` and
`error` that no longer exist.
Covered by tests/screencopy_blocking_test.cpp on a virtual clock: synchronous
and pumped completion, timeout with cancellation, no wait longer than the
remaining budget, a single wait that consumes the whole budget, dispatch
failure, and a completion fired after the call returned leaving the caller's
frame and error untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… explicit
Review feedback: the fallback added to LogindService was copy-pasted into
BrightnessService. Both now call logind::resolveSession() from logind-owned
code (src/dbus/logind/logind_session.{h,cpp}), which returns the session path
together with the lookup that answered — XDG_SESSION_ID, pid, or the user's
Display session. Both call sites log that source, so which path a session came
from is visible in the log rather than inferred.
The Display fallback is best-effort by nature: logind picks it per user, not
per caller, so a user with several concurrent sessions can be handed one this
process is not running in. That is now explicit — the resolver reads the
user's full Sessions list and warns, naming the session it guessed, instead of
returning it silently. BrightnessService keeps its "auto" path for the case
where nothing resolves at all.
The lookups are injectable, so tests/logind_session_test.cpp covers the order
(id wins, no fallback consulted), a stale XDG_SESSION_ID falling through to
the pid, the systemd-user-manager case that motivated the fallback, the
multi-session guess, a user with no display session, and every lookup dark.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks for the review — all three addressed in the two follow-up commits.
Fixed — the roundtrip is gone.
The last case is the one you asked about specifically: completion state now lives in a
Done —
Agreed. The resolver now reads the user's full The lookups are injectable, so Left as two follow-up commits so the review diff stays readable — happy to squash into the originals if you'd prefer. |
Summary
Three independent fixes to session/lock reliability, found while running Noctalia as my daily shell on Fedora + Hyprland. Each is a separate commit and they can be reviewed (or cherry-picked) independently:
fix(capture)— bound the blocking screencopy wait so a frame that never completes cannot freeze the shell.fix(lockscreen)— log lock requests that are dropped because a lock is already active.fix(logind)— resolve the logind session via the user's Display session when the PID lookup fails.Motivation
1. Blocking screencopy can spin forever
captureOutputBlocking()loopedwl_display_roundtrip()with no deadline. When the compositor never deliversready/failedfor a frame — which I reproduced with lock-screen desktop snapshots on Hyprland, on both outputs, every time — the loop spins forever insideLockScreen::lock()on the main loop. The whole shell freezes, IPC goes dark, and the session lock is never even requested, so the screen stays unlocked.Now bounded to 2 seconds, cancelling the in-flight capture on timeout (the completion callback holds stack references), letting the caller fall back to the wallpaper background.
2. Silently ignored lock requests
lock()returnedtruesilently whenisActive(). A stalem_lockedflag — which I saw after an external locker plus a compositor lock-restore sequence — then made every subsequent lock request look successful while no-op'ing. Logging the state makes the condition diagnosable; a shell restart clears it.3. logind session resolution under the systemd user manager
Both session lookups assumed the shell runs inside the login session's cgroup. Started from the systemd user manager it does not:
user@.servicesits outside that cgroup, soGetSessionByPIDanswersNoSessionForPID, andXDG_SESSION_IDis not in that manager's environment either.The whole logind integration was dark as a result: the session lock monitor logged
disabled: session path unavailable, sologinctl lock-sessionnever reached the lock screen; the idle-inhibit monitor never armed; and brightness fell back to theautosession path, which resolves against the caller and is the same dead end. Asking logind for this user's Display session as a last resort fixes all three, which is whybrightness_service.cppcarries the same fallback in that commit.Type of Change
Related Issue
None directly. Companion PR: #3908 (PAM fork deadlock, closes #3848).
Testing
just build release— clean, no new warnings, with these three commits applied directly on top ofmainafter the split.clang-formatv22.1.8 run over every touched file.logind session lock monitor active (/org/freedesktop/login1/session/_33)where it previously readdisabled: session path unavailable.Manual Coverage
Single machine only — MacBookPro15,1, Fedora 44, Hyprland, two external 4K outputs plus the internal panel. I have no Niri or Sway install to verify against. The capture fix is a deadline on an existing wait and is compositor-independent in nature; the logind fix is specific to being started from the systemd user manager rather than to any compositor.
Screenshots / Videos
Not applicable — no UI, visual, or layout changes.
Checklist
CONTRIBUTING.md.just formatwith clang-format v22+ installed, or this PR has no code changes.assets/translations/en.json, or this PR adds no new user-facing strings.Additional Notes
The 2-second screencopy deadline is a chosen constant. If you would rather it were configurable, or a different default, that is an easy change.
Happy to split these three further if you would prefer one PR per fix.