@@ -27,14 +27,6 @@ pub struct QueryInner<T> {
2727
2828 retry_strategy : Option < Box < dyn Fn ( u32 ) -> Option < Duration > > > ,
2929 retry_count : u32 ,
30-
31- /// Shared state for strategy implementations
32- /// Source ID for pending debounce timer
33- debounce_source_id : Option < glib:: SourceId > ,
34- /// Instant of the last throttled fetch execution
35- last_throttle_time : Option < Instant > ,
36- /// Source ID for pending trailing throttle timer
37- throttle_trailing_source_id : Option < glib:: SourceId > ,
3830}
3931
4032impl < T > QueryInner < T > {
@@ -60,9 +52,6 @@ impl<T> QueryInner<T> {
6052 timeout,
6153 retry_strategy : None ,
6254 retry_count : 0 ,
63- debounce_source_id : None ,
64- last_throttle_time : None ,
65- throttle_trailing_source_id : None ,
6655 }
6756 }
6857
@@ -190,16 +179,6 @@ impl<T> Drop for Query<T> {
190179 source_id. remove ( ) ;
191180 }
192181
193- // Remove any pending debounce timer
194- if let Some ( source_id) = self . inner . borrow_mut ( ) . debounce_source_id . take ( ) {
195- source_id. remove ( ) ;
196- }
197-
198- // Remove any pending throttle trailing timer
199- if let Some ( source_id) = self . inner . borrow_mut ( ) . throttle_trailing_source_id . take ( ) {
200- source_id. remove ( ) ;
201- }
202-
203182 // Abort any active fetch task to ensure cleanup
204183 if let Some ( handle) = self . inner . borrow_mut ( ) . fetch_task_handle . take ( ) {
205184 debug ! ( resource_key = %self . inner. borrow( ) . key, "Dropping last reference to Query, aborting active fetch task" ) ;
@@ -243,84 +222,95 @@ where
243222 /// Strategy: Debounce fetch calls
244223 /// Waits for `duration` after the last call before executing.
245224 /// If another call arrives before the timer fires, the timer resets.
225+ ///
226+ /// State is managed internally by the strategy and not stored in Query.
246227 pub fn debounce ( duration : Duration ) -> impl FnOnce ( & Query < T > ) {
228+ // Strategy state: managed by the closure itself
229+ let debounce_state: Rc < RefCell < Option < glib:: SourceId > > > = Rc :: new ( RefCell :: new ( None ) ) ;
230+
247231 move |query : & Query < T > | {
248232 let key = { query. inner . borrow ( ) . key . clone ( ) } ;
249233
250234 // Cancel any existing debounce timer
251- if let Some ( source_id) = query . inner . borrow_mut ( ) . debounce_source_id . take ( ) {
235+ if let Some ( source_id) = debounce_state . borrow_mut ( ) . take ( ) {
252236 debug ! ( resource_key = %key, "Cancelling previous debounce timer" ) ;
253237 source_id. remove ( ) ;
254238 }
255239
256240 let weak = Rc :: downgrade ( & query. inner ) ;
241+ let state_for_callback = debounce_state. clone ( ) ;
257242 let source_id = glib:: timeout_add_local_once ( duration, move || {
258243 if let Some ( inner) = weak. upgrade ( ) {
259244 let query = Query { inner } ;
260245 let key = { query. inner . borrow ( ) . key . clone ( ) } ;
261246 debug ! ( resource_key = %key, "Debounce timer fired, executing fetch" ) ;
262247 // Clear the source_id since timer has fired
263- query . inner . borrow_mut ( ) . debounce_source_id = None ;
248+ * state_for_callback . borrow_mut ( ) = None ;
264249 query. fetch ( ) ;
265250 }
266251 } ) ;
267252
268253 debug ! ( resource_key = %key, duration_ms = duration. as_millis( ) , "Scheduled debounced fetch" ) ;
269- query . inner . borrow_mut ( ) . debounce_source_id = Some ( source_id) ;
254+ * debounce_state . borrow_mut ( ) = Some ( source_id) ;
270255 }
271256 }
272257
273258 /// Strategy: Throttle fetch calls
274259 /// Executes at most once per `interval`.
275260 /// If `trailing` is true, a trailing fetch will be scheduled after the interval
276261 /// if calls arrived during the throttle period.
262+ ///
263+ /// State is managed internally by the strategy and not stored in Query.
277264 pub fn throttle ( interval : Duration , trailing : bool ) -> impl FnOnce ( & Query < T > ) {
265+ // Strategy state: managed by the closure itself
266+ let throttle_state: Rc < RefCell < ( Option < Instant > , Option < glib:: SourceId > ) > > =
267+ Rc :: new ( RefCell :: new ( ( None , None ) ) ) ;
268+
278269 move |query : & Query < T > | {
279270 let key = { query. inner . borrow ( ) . key . clone ( ) } ;
280271 let now = Instant :: now ( ) ;
281272
282- let last_throttle_time = { query. inner . borrow ( ) . last_throttle_time } ;
273+ let last_throttle_time = { throttle_state. borrow ( ) . 0 } ;
274+
283275 let should_fetch = match last_throttle_time {
284276 None => true ,
285277 Some ( last_time) => now. duration_since ( last_time) >= interval,
286278 } ;
287279
288280 if should_fetch {
289281 // Cancel any pending trailing timer since we're fetching now
290- if let Some ( source_id) = query. inner . borrow_mut ( ) . throttle_trailing_source_id . take ( )
291- {
282+ if let Some ( source_id) = throttle_state. borrow_mut ( ) . 1 . take ( ) {
292283 debug ! ( resource_key = %key, "Cancelling trailing throttle timer (immediate fetch)" ) ;
293284 source_id. remove ( ) ;
294285 }
295286
296287 debug ! ( resource_key = %key, "Throttle allows fetch, executing immediately" ) ;
297- query . inner . borrow_mut ( ) . last_throttle_time = Some ( now) ;
288+ * throttle_state . borrow_mut ( ) = ( Some ( now) , None ) ;
298289 query. fetch ( ) ;
299290 } else if trailing {
300291 // Schedule a trailing fetch if not already scheduled
301- let has_pending_trailing =
302- { query. inner . borrow ( ) . throttle_trailing_source_id . is_some ( ) } ;
292+ let has_pending_trailing = { throttle_state. borrow ( ) . 1 . is_some ( ) } ;
303293
304294 if !has_pending_trailing {
305295 let remaining = interval
306296 . checked_sub ( now. duration_since ( last_throttle_time. unwrap ( ) ) )
307297 . unwrap_or ( Duration :: ZERO ) ;
308298
309299 let weak = Rc :: downgrade ( & query. inner ) ;
300+ let state_for_callback = throttle_state. clone ( ) ;
310301 let source_id = glib:: timeout_add_local_once ( remaining, move || {
311302 if let Some ( inner) = weak. upgrade ( ) {
312303 let query = Query { inner } ;
313304 let key = { query. inner . borrow ( ) . key . clone ( ) } ;
314305 debug ! ( resource_key = %key, "Trailing throttle timer fired, executing fetch" ) ;
315306 // Clear the source_id and update throttle time
316- query. inner . borrow_mut ( ) . throttle_trailing_source_id = None ;
317- query. inner . borrow_mut ( ) . last_throttle_time = Some ( Instant :: now ( ) ) ;
307+ * state_for_callback. borrow_mut ( ) = ( Some ( Instant :: now ( ) ) , None ) ;
318308 query. fetch ( ) ;
319309 }
320310 } ) ;
321311
322312 debug ! ( resource_key = %key, remaining_ms = remaining. as_millis( ) , "Scheduled trailing throttle fetch" ) ;
323- query . inner . borrow_mut ( ) . throttle_trailing_source_id = Some ( source_id) ;
313+ throttle_state . borrow_mut ( ) . 1 = Some ( source_id) ;
324314 } else {
325315 debug ! ( resource_key = %key, "Throttled: trailing timer already pending" ) ;
326316 }
0 commit comments