feat(server): expose NetworkAutoDetect RTT via a shared handle#1346
feat(server): expose NetworkAutoDetect RTT via a shared handle#1346Greg Lamberson (glamberson) wants to merge 1 commit into
Conversation
The server measures network RTT in its AutoDetectManager, but after run() takes ownership a backend can no longer call rtt_snapshot(): the measurement is stranded in the running server. Mirror the existing display_suppressed handle so the value can be shared out. Add an Arc<AtomicU32> holding the latest measured RTT in milliseconds (u32::MAX until the first measurement), a with_autodetect_rtt_handle builder injector, and an autodetect_rtt_handle() getter. The AutoDetectRsp handler stores the measured rtt_ms into it. A display backend can then read a fresh, frame-traffic-independent network RTT for flow control without polling the server object. Drop the cfg_attr(egfx) gate on the new() too_many_arguments expect: the added parameter makes the non-egfx parameter count 8 (was 7), so the lint now fires in both feature configs and the expect is satisfied unconditionally.
There was a problem hiding this comment.
Pull request overview
This PR exposes the server’s NetworkAutoDetect RTT measurement via a shared Arc<AtomicU32> handle so display backends can read a fresh RTT value even after run() takes ownership of the server.
Changes:
- Add
autodetect_rtt: Arc<AtomicU32>toRdpServer, plus anautodetect_rtt_handle()getter and builder injectionwith_autodetect_rtt_handle(...). - Update the
AutoDetectRsphandler to store the latest measuredrtt_msinto the shared atomic handle. - Add tests to cover the
u32::MAXsentinel default and builder handle injection behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
crates/ironrdp-server/src/server.rs |
Adds the shared RTT handle to RdpServer, exposes it via an accessor, and updates it on auto-detect responses. |
crates/ironrdp-server/src/builder.rs |
Extends the builder to allow injecting a shared RTT handle used by both server and backend. |
crates/ironrdp-testsuite-core/tests/server/autodetect.rs |
Adds tests for the new RTT handle API and sentinel behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| autodetect: None, | ||
| connection_handler, | ||
| display_suppressed: display_suppressed.unwrap_or_else(|| Arc::new(AtomicBool::new(false))), | ||
| autodetect_rtt: autodetect_rtt.unwrap_or_else(|| Arc::new(AtomicU32::new(u32::MAX))), | ||
| } |
There was a problem hiding this comment.
True, we could load the sentinel RTT when the user provides its own handle
| let handle = Arc::new(AtomicU32::new(42)); | ||
| let server = RdpServer::builder() | ||
| .with_addr(SocketAddr::from((Ipv4Addr::LOCALHOST, 0))) | ||
| .with_no_security() | ||
| .with_no_input() | ||
| .with_no_display() | ||
| .with_autodetect_rtt_handle(Arc::clone(&handle)) | ||
| .build(); | ||
|
|
||
| assert!(Arc::ptr_eq(&handle, &server.autodetect_rtt_handle())); | ||
| assert_eq!(server.autodetect_rtt_handle().load(Ordering::Relaxed), 42); | ||
| } |
| autodetect: None, | ||
| connection_handler, | ||
| display_suppressed: display_suppressed.unwrap_or_else(|| Arc::new(AtomicBool::new(false))), | ||
| autodetect_rtt: autodetect_rtt.unwrap_or_else(|| Arc::new(AtomicU32::new(u32::MAX))), | ||
| } |
| let handle = Arc::new(AtomicU32::new(42)); | ||
| let server = RdpServer::builder() | ||
| .with_addr(SocketAddr::from((Ipv4Addr::LOCALHOST, 0))) | ||
| .with_no_security() | ||
| .with_no_input() | ||
| .with_no_display() | ||
| .with_autodetect_rtt_handle(Arc::clone(&handle)) | ||
| .build(); | ||
|
|
||
| assert!(Arc::ptr_eq(&handle, &server.autodetect_rtt_handle())); | ||
| assert_eq!(server.autodetect_rtt_handle().load(Ordering::Relaxed), 42); | ||
| } |
Benoît Cortier (CBenoit)
left a comment
There was a problem hiding this comment.
This is looking good to me, but I have a question for you.
Using an Arc<AtomicU32> to share a state is of course perfectly fine, but don’t you end up polling the value repeatedly? I was wondering if a callback-based API would not be superior.
I don’t have the full consumer context, so maybe a simple Arc<Atomic*> is enough and does not perform poorly at all (e.g.: it’s optimal for a "read on a per-need basis model).
| clippy::too_many_arguments, | ||
| reason = "called via the builder; positional parameters are an internal detail" | ||
| )] | ||
| pub fn new( |
There was a problem hiding this comment.
thought: We need to make this function private
Summary
The server measures network RTT in its AutoDetectManager (added in #1177), but once run() takes ownership a backend can no longer call rtt_snapshot() to read it: the measurement is stranded in the running server. This mirrors the existing display_suppressed shared-handle pattern (#1319) so the value can be shared out.
Adds an Arc holding the latest measured RTT in milliseconds (u32::MAX until the first measurement, and while auto-detect is disabled), a with_autodetect_rtt_handle builder injector, and an autodetect_rtt_handle() getter. The AutoDetectRsp handler stores the measured rtt_ms into it. A display backend can then read a fresh, frame-traffic-independent network RTT for flow control (for example to size an encoder's in-flight window) without polling the server object.
This also drops the cfg_attr(egfx) gate on new()'s too_many_arguments expect: the added parameter makes the non-egfx parameter count 8 (it was exactly 7, where the lint was silent), so the lint now fires in both feature configs and the expect is satisfied unconditionally.
Validation
cargo xtask check fmt/lints/tests/typos/locks all pass. New tests in ironrdp-testsuite-core cover the u32::MAX default and that with_autodetect_rtt_handle round-trips the same Arc.
Notes
Additive; mirrors #1319's handle pattern. new() gains a parameter the same way #1319 added display_suppressed (feat, not feat!, per that precedent). Exposes only the generic RTT value; no flow-control policy.