@@ -2089,55 +2089,6 @@ impl StreamingClient {
20892089 }
20902090 }
20912091
2092- /// Drain events through `on_event`, applying a caller-supplied
2093- /// [`crate::streaming::wait::WaitStrategy`] on each momentarily
2094- /// empty ring instead of the default low-latency wait.
2095- ///
2096- /// This is the Rust-native bring-your-own-strategy escape hatch: the
2097- /// default wait ([`crate::streaming::wait::BusySpinWithSpinLoopHint`]-style
2098- /// spin) suits real-time market data, but a Rust caller with an
2099- /// exotic backoff (e.g. an adaptive PID-controlled park, or a
2100- /// strategy that coordinates with another subsystem) can supply any
2101- /// `W: WaitStrategy` here. Use a strategy from
2102- /// [`crate::streaming::wait`] (e.g.
2103- /// [`crate::streaming::wait::BusySpin`]) or implement the trait on
2104- /// your own type.
2105- ///
2106- /// `W` is monomorphised into the loop, so the per-poll cost is the
2107- /// caller's `wait_for` body with no indirection. Delivery semantics
2108- /// match [`Self::for_each`]: `on_event` fires exactly once per event
2109- /// and the loop returns on terminal shutdown after the ring drains.
2110- ///
2111- /// # Why Rust-only
2112- ///
2113- /// `wait_for` fires on every ring-empty poll on the hot path. Routing
2114- /// that per-poll callback across the C ABI, the CPython interpreter
2115- /// lock, or the JavaScript event loop would add call-boundary
2116- /// overhead to the single tightest loop in the consumer — a latency
2117- /// regression, not a tuning knob. The bindings therefore run the
2118- /// fixed low-latency wait with no override.
2119- pub fn for_each_with_wait_strategy < W , F > ( & self , mut on_event : F , strategy : W ) -> PollOutcome
2120- where
2121- W : crate :: streaming:: wait:: WaitStrategy ,
2122- F : FnMut ( & StreamEvent ) ,
2123- {
2124- // The drain owner + CPU pin are recorded inside `poll_batch`, after
2125- // the staging lock is acquired, so the identity reflects the proven
2126- // drainer rather than whichever thread merely entered this loop.
2127- loop {
2128- match self . poll_batch ( & mut on_event) {
2129- // Return the terminal outcome so the caller can tell a clean
2130- // shutdown from an I/O-thread fault (`Failed`).
2131- terminal @ ( PollOutcome :: Shutdown | PollOutcome :: Failed ) => return terminal,
2132- // `Busy` cannot arise from this loop's own poll (it holds no
2133- // lock when it polls); back off and retry defensively in case
2134- // an `on_event` callback re-entered a drain.
2135- PollOutcome :: Drained ( 0 ) | PollOutcome :: Busy => strategy. wait_for ( 0 ) ,
2136- PollOutcome :: Drained ( _) => { }
2137- }
2138- }
2139- }
2140-
21412092 /// Polymorphic subscribe — wire-level entry point.
21422093 ///
21432094 /// Accepts a typed [`protocol::Subscription`] value built via
@@ -4144,35 +4095,6 @@ mod ring_occupancy_tests {
41444095 ) ;
41454096 }
41464097
4147- /// The bring-your-own-strategy drain is reachable through the
4148- /// crate-owned wait-strategy re-export, so a Rust caller never names
4149- /// the underlying ring crate. Dropping the producer terminates the
4150- /// drain; `BusySpin`'s `wait_for` is a no-op, so the loop spins only
4151- /// until the ring reports shutdown.
4152- #[ test]
4153- fn for_each_with_wait_strategy_accepts_crate_owned_preset ( ) {
4154- use crate :: streaming:: wait:: BusySpin ;
4155-
4156- let ( client, mut producer) = StreamingClient :: for_ring_occupancy_test ( 64 ) ;
4157- for _ in 0 ..3 {
4158- assert ! (
4159- publish_one( & mut producer) ,
4160- "fresh ring must accept a publish"
4161- ) ;
4162- }
4163- // Drop the producer so the poller observes terminal shutdown once
4164- // the three published events drain, and the loop returns.
4165- drop ( producer) ;
4166-
4167- let mut delivered = 0usize ;
4168- client. for_each_with_wait_strategy ( |_event| delivered += 1 , BusySpin ) ;
4169-
4170- assert_eq ! (
4171- delivered, 3 ,
4172- "every published event must be delivered before shutdown returns"
4173- ) ;
4174- }
4175-
41764098 /// A faulted I/O thread — the `io_loop` unwound and its fault guard set
41774099 /// `io_faulted` before the ring published its shutdown sequence — must
41784100 /// surface through the CALLBACK drain path, not read as a clean shutdown.
0 commit comments