drm: address the self-review findings on the review rework

Five defects an adversarial pass found in the previous commits:
- refresh_available_async set the single-flight probe guard, then relied on the
  detached thread to clear it; if thread creation failed (EAGAIN) or the closure
  unwound, the guard leaked true and froze every future probe. Release it via RAII
  inside the closure and on a Builder::spawn error.
- The _drm per-frame re-auth called the cached active_uid(), which on a cache miss
  (exactly during a session switch) falls back to a blocking loginctl seat0 lookup --
  on the single-threaded _drm runtime, once per frame, a subprocess storm. Use a new
  cache-only accessor that never blocks and fails closed on a miss, and correct the
  comment: the stop is bounded by the active-uid cache cadence, not one frame.
- set_drm_cursor inserted unconditionally, so a still-draining predecessor stream
  could overwrite (then delete on teardown) the cursor a replacement stream published
  for the same index. Make it a compare-and-set that ignores an older epoch.
- recv_msg_timeout2 treated a spurious readable() wakeup with nothing consumed as a
  mid-frame stall and tore the stream down. Track whether any byte was consumed
  (drm_read_full sets it) and map a zero-progress deadline back to None (re-poll),
  reserving the hard error for a genuine partial-frame stall.
This commit is contained in:
Mariano Abad
2026-07-21 16:11:43 -03:00
parent 809ee1a7f1
commit 163e0ac42f
4 changed files with 109 additions and 31 deletions

View File

@@ -976,6 +976,15 @@ pub fn get_active_userid_fresh() -> String {
get_values_of_seat0(&[1])[0].clone()
}
#[inline]
/// The cached active uid as a number, or `None` when the cache is empty. Unlike `get_active_userid`
/// this NEVER falls back to a blocking `loginctl` seat0 lookup, so it is safe to call on an async
/// runtime thread and on a hot path (e.g. per-frame re-auth): a cache miss returns `None` for the
/// caller to treat as "active session momentarily unknown" rather than stalling on a subprocess.
pub fn get_active_userid_cached() -> Option<u32> {
get_active_user_id_name_from_cache().and_then(|(uid, _)| uid.parse::<u32>().ok())
}
fn get_cm() -> bool {
// We use `CMD_PS` instead of `ps` to suppress some audit messages on some systems.
if let Ok(output) = Command::new(CMD_PS.as_str()).args(vec!["aux"]).output() {