drm: close the round-14 review findings

- the .so contract and deb assertions no longer pipe into grep. under
  `set -o pipefail`, `producer | grep -q` reports a FALSE FAILURE once
  the producer outruns the 64 KB pipe buffer: grep -q exits at the
  first match, the producer dies on SIGPIPE, and pipefail makes that
  the pipeline's status - so a library that HAS the symbol is reported
  as missing it and the step fails on a good build. measured on a real
  EGL-enabled .so (101 KB of strings, both markers present): the piped
  form reported both missing. this was introduced by the strictness
  fix two rounds ago and only passes today because a release-sized .so
  fits in the buffer. NOTE the obvious repair does not work either -
  materializing the output and piping the variable keeps the pipe and
  fails identically (measured), so these now match with bash's own
  pattern operator and no subprocess at all. verified with positive
  and negative controls.
- warm_availability decides X11 for itself, inside its retry loop,
  with the UNMEMOISED `scrap::is_x11()`. this is the same one-shot-at
  -startup bug the pre-warm had, in its sibling call site, left behind
  when that one was fixed: the check ran during startup, where
  loginctl cannot yet name the seat0 session and the answer defaults
  to "x11", so a Wayland host that came up slowly skipped the warm for
  the life of the process and got back the cold-probe "No displays"
  symptom the warm exists to remove. the memoised form would have
  moved the bug rather than fixed it, since it latches its first
  answer.
- the grab_desc SAFETY comment says what the frame protocol actually
  is instead of promising a release on every return path: traced in
  the C, a failing grab_desc leaves nothing to release (-EINVAL
  returns before allocating, a failed inner grab has already cleaned
  up, and -ENOTSUP releases the frame itself), so releasing on those
  paths would be a double free.
This commit is contained in:
Mariano Abad
2026-07-30 16:57:09 -03:00
parent ad246d2714
commit 78bfe61554
4 changed files with 66 additions and 14 deletions

View File

@@ -603,12 +603,15 @@ pub async fn start_server(is_server: bool, no_server: bool) {
});
// Warm the DRM availability cache before any client connects, so the first connection does
// not race a cold `_drm` probe and ship an empty display list ("No displays" + retry).
// Skipped on X11: every consumer of the verdict is behind an `!is_x11()` check, so probing
// there makes the root service open DRM readers for a path this session can never use.
// X11 is skipped -- probing there makes the root service open DRM readers for a path this
// session can never take -- but that decision belongs to `warm_availability`, which already
// makes it, and NOT to this call site. Deciding it here is the same one-shot-at-startup
// mistake the pre-warm had: `is_x11()` answers "x11" whenever loginctl cannot yet name the
// seat0 session, which during a boot is exactly when this runs, and nothing revisits it --
// so a Wayland host that came up slowly skipped the warm for the life of the process and
// got back the cold-probe "No displays" symptom the warm exists to remove.
#[cfg(all(target_os = "linux", feature = "drm"))]
if !scrap::is_x11() {
std::thread::spawn(drm_capturer::warm_availability);
}
std::thread::spawn(drm_capturer::warm_availability);
input_service::fix_key_down_timeout_loop();
#[cfg(target_os = "linux")]
if input_service::wayland_use_uinput() {

View File

@@ -1411,10 +1411,28 @@ pub(super) fn warm_availability() {
// Nothing on X11 can consume a DRM stream, and probing makes the ROOT service open DRM readers,
// so an X11 host running a drm build would pay that at every startup for a path it can never
// take. The lazy probe behind is_available is reached only from the Wayland paths already.
if crate::platform::linux::is_x11() {
return;
}
//
// Two things about WHERE and HOW this is decided, both learned the hard way on the pre-warm's
// identical gate (see `drm_prewarm` in ipc/drm.rs):
//
// - it is decided HERE, not at the call site in server.rs. This runs during startup, and
// `get_display_server()` answers "x11" whenever loginctl cannot yet name the seat0 session,
// which during a boot is precisely then. A one-shot check outside the retry loop skipped the
// warm for the life of the process on a Wayland host that came up slowly, handing back the
// cold-probe "No displays" symptom this function exists to remove.
// - it uses `scrap::is_x11()`, the UNMEMOISED form that re-runs loginctl per call.
// `crate::platform::linux::is_x11()` latches its first answer in a lazy_static, so asking it
// inside a retry loop would re-read the same early "x11" ten times and change nothing.
//
// The re-check rides the existing retry loop rather than adding a second wait: a genuine X11
// host spends the same ten short attempts it already spent on `query_displays` and opens
// nothing, and a host whose session turns out to be Wayland proceeds on the attempt where that
// becomes knowable.
for _ in 0..10 {
if scrap::is_x11() {
std::thread::sleep(Duration::from_millis(300));
continue;
}
if matches!(&*DRM_STATE.lock().unwrap(), ProbeState::Available(..)) {
return;
}