refactor(device): take a wasm-portable timer instead of tokio::time - #840
Open
4ni1ak wants to merge 1 commit into
Open
refactor(device): take a wasm-portable timer instead of tokio::time#8404ni1ak wants to merge 1 commit into
4ni1ak wants to merge 1 commit into
Conversation
`openlogi-device` timed its inventory probes, writes and pairing session with `tokio::time`. That compiles for `wasm32-unknown-unknown` and panics the first time a timer is polled, and the `wasm (portable crates)` job is a `cargo check`, so nothing caught it — the crate was on the portable list while carrying a timer that cannot run there. `futures-timer` is the timer both targets share, and it was already in the graph: `openlogi-hidpp`, the other crate on that list, times its own reads with it. So the global timer thread it costs on native is paid whether or not this crate joins, which is what settles the trade-off the issue names. The cfg lives in the manifest, not in the code: `src/time.rs` exposes `sleep` and `timeout` and no call site learns which target it is on. `timeout` is written once on top of `sleep` because futures-timer ships no such combinator — polling the future ahead of the timer, so an already-ready future still wins a zero-length budget the way the probe paths assume. `futures-timer` only reaches for the browser's timer under its `wasm-bindgen` feature; without it a wasm build silently gets the thread-backed native path, which is the same runtime failure in a different crate. The feature is enabled for the wasm target only, so native builds pull neither `gloo-timers` nor `send_wrapper` — and since Cargo unifies features, `openlogi-hidpp`'s timers get the browser path on wasm too. Fixes AprilNEA#826
Greptile SummaryThis PR replaces
Confidence Score: 5/5The PR appears safe to merge, with no concrete regressions identified in timer semantics, target feature selection, or migrated call sites. The portable timeout preserves operation-first tie handling and drop-based cancellation, while Cargo’s target-specific feature resolution selects the browser timer backend for the configured wasm build without affecting native builds.
|
| Filename | Overview |
|---|---|
| crates/openlogi-device/src/time.rs | Introduces portable sleep and timeout helpers whose tie-breaking and cancellation behavior match the affected call sites. |
| crates/openlogi-device/Cargo.toml | Adds futures-timer and enables its browser backend only for wasm32 targets under Cargo resolver v3. |
| crates/openlogi-device/src/inventory.rs | Migrates inventory probe timeout and retry delay usage to the shared timer abstraction. |
| crates/openlogi-device/src/inventory/probe.rs | Migrates probe and arrival-drain timeout operations while preserving opaque timeout handling. |
| crates/openlogi-device/src/pairing.rs | Replaces the pinned Tokio pairing deadline with the portable delay future. |
| crates/openlogi-device/src/session/gesture.rs | Uses the portable delay for periodic liveness checks inside the existing select loop. |
| crates/openlogi-device/src/session/host_switch.rs | Moves channel-open and HID++ operation budgets to the shared timeout helper. |
| crates/openlogi-device/src/write/litra.rs | Migrates raw report write timing while retaining the existing timeout error mapping. |
| Cargo.lock | Records the browser timer dependencies introduced by the wasm-enabled futures-timer feature. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Callers[Inventory, pairing, sessions, and writes] --> Time[openlogi-device time abstraction]
Time --> Sleep[futures_timer Delay]
Time --> Timeout[left-biased timeout combinator]
Timeout --> Sleep
Sleep --> Native[Native timer backend]
Sleep --> Wasm[Browser timer backend on wasm32]
Reviews (1): Last reviewed commit: "refactor(device): take a wasm-portable t..." | Re-trigger Greptile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
openlogi-devicetimed its inventory probes, writes and pairing session withtokio::time, which compiles forwasm32-unknown-unknownand panics the firsttime a timer is polled. The
wasm (portable crates)job is acargo check, sothe crate sat on the portable list carrying a timer that cannot run there.
Fixes #826
The choice
The issue frames it as
futures-timer— which costs a global timer thread onnative — against a cfg-split shim. The tie-breaker is that
futures-timerisalready in the graph:
openlogi-hidpp, the other crate on the portable list,times its own reads with it, so every native build that links
openlogi-devicepays for that thread whether or not this crate joins. Taking it costs nothing
new and removes the cfg entirely.
So the split lives in the manifest, not the code.
src/time.rsexposessleepand
timeout; no call site learns which target it is on, which is theconstraint the issue sets.
Changes
openlogi-device/src/time.rs(new)sleepwrapsfutures_timer::Delay.timeoutis written once on top of it, because futures-timer ships notimeout combinator — that is the reason this module exists rather than each
call site reaching for
Delay. The future is polled ahead of the timer, soone that is already ready still wins a zero-length budget, matching what
tokio::time::timeoutdocuments and what the probe paths rely on when acached answer resolves without suspending.
Elapsedis a unit error: every caller in this crate already treated atimeout as one opaque outcome (
Err(_),.is_err(),.ok()).Call sites — eleven, across
inventory,inventory::probe,session::{gesture, host_switch, keyboard},write::{lighting, litra, smartshift}andpairing, plus one test harness guard.grep tokio::timeover the crate now returns only two doc references in the new module.
tokio::syncis untouched; it works on wasm.Cargo.tomlfutures-timer = "3.0.4", matching the versionopenlogi-hidpppins.wasm-bindgenfeature enabled for the wasm target only. Without itfutures-timer hands a wasm build its native, thread-backed path — the same
runtime failure this change is removing, in a different crate. Scoping it to
the target keeps
gloo-timersandsend_wrapperout of native builds, andsince Cargo unifies features it also gives
openlogi-hidpp's timers thebrowser path on wasm.
Testing
Linux, x86_64, Rust 1.98.0:
All green; 173 tests in
openlogi-device.The feature scoping was checked directly rather than assumed:
src/time.rscarries three tests: budget met, already-ready future against azero budget, and a pending future elapsing.
What this does not prove. The panic it removes is a runtime one on wasm,
and there is no wasm runtime here —
cargo checkis as far as CI or this hostgoes, which is the gap the issue itself names. What is verified is that the
crate no longer references
tokio::time, that the browser timer is the oneselected for wasm builds, and that native behaviour is unchanged by the test
suite. Real confirmation belongs with whatever first drives this crate from a
browser (#825).
Not run on this host:
tests (macos),cargo-deny, macOS clippy.No hardware verification applies — no device code changed, only the timers
around it.