From a53369bf21e5ed0297fdda968d6c73a983a08aca Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 20 Jul 2026 11:49:31 +0200 Subject: [PATCH] =?UTF-8?q?fix(ci):=20green=20main=20again=20=E2=80=94=20c?= =?UTF-8?q?lock=5Fsync=20callers,=20two=20clippy=20denials,=20an=20unused?= =?UTF-8?q?=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ci.yml`'s clippy gate has been failing on main since the pre-0.16.0 sweep landed, and because clippy stops at the first crate it can't compile, the visible error was only ever the first of four. `ci.yml` is not tag-triggered, so v0.16.0 cut and shipped over a red main; none of this reaches the release artifacts (the two E0308s are in a test binary and a dev tool, and the rest are lints) but the gate has been blind since. Fixed, in the order clippy surfaced them: - clients/probe failed to COMPILE (E0308, x2). `810d918d` moved `clock_sync` onto the resumable `io::MsgReader` but only updated the client pump, leaving the probe passing a bare `&mut RecvStream`. The probe now wraps the control stream in a `MsgReader` at `open_bi` and threads that everywhere — Welcome, the --remode and --bitrate watchers, and the speed-test result read — which is also what the refactor was for: those reads sit behind timeouts and `select!`, exactly where a straddling frame would desync the stream for the rest of the run. - pf-capture: `SPA_META_Cursor as u32` is a `u32 -> u32` no-op (`clippy::unnecessary_cast`). Line 1274 already passes the same constant uncast, so the type is not in question. - pf-inject: `noop as usize` on the SIGUSR1 wake handler added in `986402f7` trips `clippy::function_casts_as_integer`; goes via `*const ()` as the lint asks. Same value in the `usize`-typed `sa_sigaction` slot. - punktfunk-core: the `ctrl_framing` test module's `use super::*` is unused, which `-D warnings` promotes to an error. Verified with CI's own commands on a Linux box (this is all Linux-gated code, so a Mac cannot check it): `cargo clippy --workspace --all-targets --locked -- -D warnings` finishes clean, `cargo build --workspace --locked` succeeds, and `cargo test --workspace --locked` exits 0 with no failures — including punktfunk-core's 196-test suite, which covers the control-stream framing the probe change touches. Co-Authored-By: Claude Opus 4.8 --- clients/probe/src/main.rs | 22 +++++++++---------- crates/pf-capture/src/linux/mod.rs | 3 +-- .../src/inject/linux/steam_gadget.rs | 6 ++++- crates/punktfunk-core/src/quic/tests.rs | 1 - 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/clients/probe/src/main.rs b/clients/probe/src/main.rs index 37c553f8..fa4bc55b 100644 --- a/clients/probe/src/main.rs +++ b/clients/probe/src/main.rs @@ -458,7 +458,11 @@ async fn session(args: Args) -> Result<()> { ), (None, None) => tracing::info!(%remote, "punktfunk/1 connected"), } - let (mut send, mut recv) = conn.open_bi().await.context("open control stream")?; + let (mut send, recv) = conn.open_bi().await.context("open control stream")?; + // Frame every read on the control stream through the resumable reader, exactly as the client + // pump does: `clock_sync` bounds each read with a timeout, and a frame straddling two wakeups + // would otherwise leave the stream permanently misaligned for the rest of the run. + let mut recv = io::MsgReader::new(recv); io::write_msg( &mut send, @@ -513,8 +517,8 @@ async fn session(args: Args) -> Result<()> { .encode(), ) .await?; - let welcome = Welcome::decode(&io::read_msg(&mut recv).await?) - .map_err(|e| anyhow!("Welcome decode: {e:?}"))?; + let welcome = + Welcome::decode(&recv.read_msg().await?).map_err(|e| anyhow!("Welcome decode: {e:?}"))?; tracing::info!( mode = ?welcome.mode, fec = ?welcome.fec, @@ -629,10 +633,7 @@ async fn session(args: Args) -> Result<()> { tracing::error!("Reconfigure write failed"); return; } - match io::read_msg(&mut rr) - .await - .map(|b| Reconfigured::decode(&b)) - { + match rr.read_msg().await.map(|b| Reconfigured::decode(&b)) { Ok(Ok(ack)) if ack.accepted => { tracing::info!(mode = ?ack.mode, "mode switch ACCEPTED") } @@ -685,10 +686,7 @@ async fn session(args: Args) -> Result<()> { tracing::error!("SetBitrate write failed"); return; } - match io::read_msg(&mut rr) - .await - .map(|b| BitrateChanged::decode(&b)) - { + match rr.read_msg().await.map(|b| BitrateChanged::decode(&b)) { Ok(Ok(ack)) => tracing::info!( applied_kbps = ack.bitrate_kbps, "BITRATE CHANGE acked by host" @@ -750,7 +748,7 @@ async fn session(args: Args) -> Result<()> { tracing::error!("ProbeRequest write failed"); return; } - let res = match io::read_msg(&mut sr).await.map(|b| ProbeResult::decode(&b)) { + let res = match sr.read_msg().await.map(|b| ProbeResult::decode(&b)) { Ok(Ok(r)) => r, other => { tracing::error!(?other, "bad ProbeResult"); diff --git a/crates/pf-capture/src/linux/mod.rs b/crates/pf-capture/src/linux/mod.rs index e5e56880..a5d68e91 100644 --- a/crates/pf-capture/src/linux/mod.rs +++ b/crates/pf-capture/src/linux/mod.rs @@ -1318,8 +1318,7 @@ mod pipewire { // that SIGSEGVs inside the PipeWire `.process` callback (a segfault `catch_unwind` cannot // catch). Every offset below is validated against `region_size` with checked arithmetic, // mirroring the fd-length guard the main frame path already applies to xdg-desktop-portal-wlr. - let meta = - unsafe { spa::sys::spa_buffer_find_meta(spa_buf, spa::sys::SPA_META_Cursor as u32) }; + let meta = unsafe { spa::sys::spa_buffer_find_meta(spa_buf, spa::sys::SPA_META_Cursor) }; if meta.is_null() { return; } diff --git a/crates/pf-inject/src/inject/linux/steam_gadget.rs b/crates/pf-inject/src/inject/linux/steam_gadget.rs index c734b1bb..d4b0c2f8 100644 --- a/crates/pf-inject/src/inject/linux/steam_gadget.rs +++ b/crates/pf-inject/src/inject/linux/steam_gadget.rs @@ -215,7 +215,11 @@ fn install_wake_handler() { // for a single signal; touches only this process's disposition for `WAKE_SIGNAL`. unsafe { let mut sa: libc::sigaction = std::mem::zeroed(); - sa.sa_sigaction = noop as usize; + // Via `*const ()`: casting a function item straight to an integer is what + // `clippy::function_casts_as_integer` rejects, and the pointer hop is the documented + // way to spell it. `sa_sigaction` is a `usize`-typed handler slot, so the value is + // unchanged. + sa.sa_sigaction = noop as *const () as usize; libc::sigemptyset(&mut sa.sa_mask); sa.sa_flags = 0; libc::sigaction(WAKE_SIGNAL, &sa, std::ptr::null_mut()); diff --git a/crates/punktfunk-core/src/quic/tests.rs b/crates/punktfunk-core/src/quic/tests.rs index dc019d5e..37237714 100644 --- a/crates/punktfunk-core/src/quic/tests.rs +++ b/crates/punktfunk-core/src/quic/tests.rs @@ -1737,7 +1737,6 @@ mod clip_loopback { /// [`io::MsgReader`] must survive that: the partial frame lives in the reader, not the future. mod ctrl_framing { use super::clip_loopback::connect_pair; - use super::*; use crate::quic::io; /// A frame whose halves land in different wakeups, with the read cancelled in between, must