1use parking_lot::RwLock;
11use std::sync::Arc;
12use std::time::{Duration, Instant};
13use thiserror::Error;
14
15#[derive(Error, Debug, Clone)]
17pub enum AdaptivePollingError {
18 #[error("Invalid polling configuration: {0}")]
19 InvalidConfig(String),
20
21 #[error("Polling interval adjustment failed: {0}")]
22 AdjustmentFailed(String),
23}
24
25#[derive(Debug, Clone)]
27pub struct AdaptivePollingConfig {
28 pub min_interval: Duration,
30
31 pub max_interval: Duration,
33
34 pub default_interval: Duration,
36
37 pub idle_increase_factor: f64,
39
40 pub active_decrease_factor: f64,
42
43 pub idle_threshold: Duration,
45
46 pub enable_sleep_mode: bool,
48
49 pub sleep_interval: Duration,
51
52 pub sleep_threshold: Duration,
54}
55
56impl Default for AdaptivePollingConfig {
57 fn default() -> Self {
58 Self {
59 min_interval: Duration::from_millis(50), max_interval: Duration::from_secs(5), default_interval: Duration::from_millis(500), idle_increase_factor: 1.5, active_decrease_factor: 0.7, idle_threshold: Duration::from_secs(10), enable_sleep_mode: true,
66 sleep_interval: Duration::from_secs(30), sleep_threshold: Duration::from_secs(60), }
69 }
70}
71
72impl AdaptivePollingConfig {
73 pub fn mobile() -> Self {
75 Self {
76 min_interval: Duration::from_millis(100),
77 max_interval: Duration::from_secs(10),
78 default_interval: Duration::from_secs(1),
79 idle_increase_factor: 2.0, active_decrease_factor: 0.6,
81 idle_threshold: Duration::from_secs(5),
82 enable_sleep_mode: true,
83 sleep_interval: Duration::from_secs(60),
84 sleep_threshold: Duration::from_secs(30),
85 }
86 }
87
88 pub fn iot() -> Self {
90 Self {
91 min_interval: Duration::from_millis(200),
92 max_interval: Duration::from_secs(30),
93 default_interval: Duration::from_secs(2),
94 idle_increase_factor: 2.5, active_decrease_factor: 0.5,
96 idle_threshold: Duration::from_secs(5),
97 enable_sleep_mode: true,
98 sleep_interval: Duration::from_secs(120), sleep_threshold: Duration::from_secs(20),
100 }
101 }
102
103 pub fn low_power() -> Self {
105 Self {
106 min_interval: Duration::from_millis(500),
107 max_interval: Duration::from_secs(60),
108 default_interval: Duration::from_secs(5),
109 idle_increase_factor: 3.0, active_decrease_factor: 0.5,
111 idle_threshold: Duration::from_secs(3),
112 enable_sleep_mode: true,
113 sleep_interval: Duration::from_secs(300), sleep_threshold: Duration::from_secs(15),
115 }
116 }
117
118 pub fn high_performance() -> Self {
120 Self {
121 min_interval: Duration::from_millis(10),
122 max_interval: Duration::from_millis(500),
123 default_interval: Duration::from_millis(50),
124 idle_increase_factor: 1.2, active_decrease_factor: 0.9,
126 idle_threshold: Duration::from_secs(30),
127 enable_sleep_mode: false, sleep_interval: Duration::from_secs(1),
129 sleep_threshold: Duration::from_secs(600),
130 }
131 }
132
133 pub fn validate(&self) -> Result<(), AdaptivePollingError> {
135 if self.min_interval.is_zero() {
136 return Err(AdaptivePollingError::InvalidConfig(
137 "Minimum interval must be > 0".to_string(),
138 ));
139 }
140
141 if self.max_interval < self.min_interval {
142 return Err(AdaptivePollingError::InvalidConfig(
143 "Maximum interval must be >= minimum interval".to_string(),
144 ));
145 }
146
147 if self.default_interval < self.min_interval || self.default_interval > self.max_interval {
148 return Err(AdaptivePollingError::InvalidConfig(
149 "Default interval must be between min and max".to_string(),
150 ));
151 }
152
153 if self.idle_increase_factor < 1.0 {
154 return Err(AdaptivePollingError::InvalidConfig(
155 "Idle increase factor must be >= 1.0".to_string(),
156 ));
157 }
158
159 if self.active_decrease_factor <= 0.0 || self.active_decrease_factor > 1.0 {
160 return Err(AdaptivePollingError::InvalidConfig(
161 "Active decrease factor must be in (0.0, 1.0]".to_string(),
162 ));
163 }
164
165 Ok(())
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum ActivityLevel {
172 High,
174 Moderate,
176 Low,
178 Idle,
180 Sleep,
182}
183
184#[derive(Debug)]
186struct PollingState {
187 current_interval: Duration,
189
190 activity_level: ActivityLevel,
192
193 last_activity: Instant,
195
196 events_in_window: u64,
198
199 window_start: Instant,
201
202 adjustments_made: u64,
204}
205
206impl PollingState {
207 fn new(default_interval: Duration) -> Self {
208 let now = Instant::now();
209 Self {
210 current_interval: default_interval,
211 activity_level: ActivityLevel::Moderate,
212 last_activity: now,
213 events_in_window: 0,
214 window_start: now,
215 adjustments_made: 0,
216 }
217 }
218}
219
220pub struct AdaptivePolling {
222 config: AdaptivePollingConfig,
223 state: Arc<RwLock<PollingState>>,
224}
225
226impl AdaptivePolling {
227 pub fn new(config: AdaptivePollingConfig) -> Result<Self, AdaptivePollingError> {
229 config.validate()?;
230
231 let state = PollingState::new(config.default_interval);
232
233 Ok(Self {
234 config: config.clone(),
235 state: Arc::new(RwLock::new(state)),
236 })
237 }
238
239 pub fn record_activity(&self) {
241 let mut state = self.state.write();
242 let now = Instant::now();
243
244 state.last_activity = now;
245 state.events_in_window += 1;
246
247 if now.duration_since(state.window_start) >= Duration::from_secs(1) {
249 let events_per_sec = state.events_in_window;
251 state.activity_level = if events_per_sec >= 10 {
252 ActivityLevel::High
253 } else if events_per_sec >= 3 {
254 ActivityLevel::Moderate
255 } else if events_per_sec >= 1 {
256 ActivityLevel::Low
257 } else {
258 ActivityLevel::Idle
259 };
260
261 state.events_in_window = 0;
262 state.window_start = now;
263 }
264 }
265
266 pub fn adjust_interval(&self) {
268 let mut state = self.state.write();
269 let now = Instant::now();
270 let time_since_activity = now.duration_since(state.last_activity);
271
272 if self.config.enable_sleep_mode && time_since_activity >= self.config.sleep_threshold {
274 state.activity_level = ActivityLevel::Sleep;
275 state.current_interval = self.config.sleep_interval;
276 state.adjustments_made += 1;
277 return;
278 }
279
280 if time_since_activity >= self.config.idle_threshold {
282 state.activity_level = ActivityLevel::Idle;
283 let new_interval = Duration::from_secs_f64(
284 state.current_interval.as_secs_f64() * self.config.idle_increase_factor,
285 );
286 state.current_interval = new_interval.min(self.config.max_interval);
287 state.adjustments_made += 1;
288 return;
289 }
290
291 match state.activity_level {
293 ActivityLevel::High => {
294 let new_interval = Duration::from_secs_f64(
295 state.current_interval.as_secs_f64() * self.config.active_decrease_factor,
296 );
297 state.current_interval = new_interval.max(self.config.min_interval);
298 state.adjustments_made += 1;
299 }
300 ActivityLevel::Moderate => {
301 let diff = state.current_interval.as_secs_f64()
303 - self.config.default_interval.as_secs_f64();
304 if diff.abs() > 0.1 {
305 let new_interval =
306 Duration::from_secs_f64(state.current_interval.as_secs_f64() - diff * 0.1);
307 state.current_interval = new_interval
308 .max(self.config.min_interval)
309 .min(self.config.max_interval);
310 state.adjustments_made += 1;
311 }
312 }
313 ActivityLevel::Low => {
314 let new_interval = Duration::from_secs_f64(
315 state.current_interval.as_secs_f64() * self.config.idle_increase_factor * 0.5,
316 );
317 state.current_interval = new_interval.min(self.config.max_interval);
318 state.adjustments_made += 1;
319 }
320 ActivityLevel::Idle | ActivityLevel::Sleep => {
321 }
323 }
324 }
325
326 pub fn current_interval(&self) -> Duration {
328 self.state.read().current_interval
329 }
330
331 pub fn activity_level(&self) -> ActivityLevel {
333 self.state.read().activity_level
334 }
335
336 pub fn time_since_activity(&self) -> Duration {
338 let state = self.state.read();
339 Instant::now().duration_since(state.last_activity)
340 }
341
342 pub fn reset(&self) {
344 let mut state = self.state.write();
345 state.current_interval = self.config.default_interval;
346 state.activity_level = ActivityLevel::Moderate;
347 state.last_activity = Instant::now();
348 state.events_in_window = 0;
349 state.window_start = Instant::now();
350 }
351
352 pub fn stats(&self) -> AdaptivePollingStats {
354 let state = self.state.read();
355 AdaptivePollingStats {
356 current_interval: state.current_interval,
357 activity_level: state.activity_level,
358 time_since_activity: Instant::now().duration_since(state.last_activity),
359 events_in_window: state.events_in_window,
360 adjustments_made: state.adjustments_made,
361 }
362 }
363}
364
365#[derive(Debug, Clone)]
367pub struct AdaptivePollingStats {
368 pub current_interval: Duration,
369 pub activity_level: ActivityLevel,
370 pub time_since_activity: Duration,
371 pub events_in_window: u64,
372 pub adjustments_made: u64,
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use std::thread;
379
380 #[test]
381 fn test_config_default() {
382 let config = AdaptivePollingConfig::default();
383 assert!(config.validate().is_ok());
384 assert_eq!(config.min_interval, Duration::from_millis(50));
385 }
386
387 #[test]
388 fn test_config_mobile() {
389 let config = AdaptivePollingConfig::mobile();
390 assert!(config.validate().is_ok());
391 assert!(config.enable_sleep_mode);
392 }
393
394 #[test]
395 fn test_config_iot() {
396 let config = AdaptivePollingConfig::iot();
397 assert!(config.validate().is_ok());
398 assert_eq!(config.max_interval, Duration::from_secs(30));
399 }
400
401 #[test]
402 fn test_config_low_power() {
403 let config = AdaptivePollingConfig::low_power();
404 assert!(config.validate().is_ok());
405 assert_eq!(config.sleep_interval, Duration::from_secs(300));
406 }
407
408 #[test]
409 fn test_config_high_performance() {
410 let config = AdaptivePollingConfig::high_performance();
411 assert!(config.validate().is_ok());
412 assert!(!config.enable_sleep_mode);
413 }
414
415 #[test]
416 fn test_config_validation_min_interval() {
417 let config = AdaptivePollingConfig {
418 min_interval: Duration::from_secs(0),
419 ..Default::default()
420 };
421 assert!(config.validate().is_err());
422 }
423
424 #[test]
425 fn test_config_validation_max_less_than_min() {
426 let config = AdaptivePollingConfig {
427 max_interval: Duration::from_millis(10),
428 min_interval: Duration::from_millis(100),
429 ..Default::default()
430 };
431 assert!(config.validate().is_err());
432 }
433
434 #[test]
435 fn test_polling_new() {
436 let config = AdaptivePollingConfig::default();
437 let polling = AdaptivePolling::new(config.clone()).unwrap();
438
439 assert_eq!(polling.current_interval(), config.default_interval);
440 assert_eq!(polling.activity_level(), ActivityLevel::Moderate);
441 }
442
443 #[test]
444 fn test_record_activity() {
445 let config = AdaptivePollingConfig::default();
446 let polling = AdaptivePolling::new(config).unwrap();
447
448 polling.record_activity();
449
450 assert!(polling.time_since_activity() < Duration::from_millis(100));
451 }
452
453 #[test]
454 fn test_adjust_interval_idle() {
455 let config = AdaptivePollingConfig {
456 idle_threshold: Duration::from_millis(100),
457 ..Default::default()
458 };
459 let polling = AdaptivePolling::new(config.clone()).unwrap();
460
461 thread::sleep(Duration::from_millis(150));
463 polling.adjust_interval();
464
465 let new_interval = polling.current_interval();
466 assert!(new_interval > config.default_interval);
467 }
468
469 #[test]
470 fn test_adjust_interval_active() {
471 let config = AdaptivePollingConfig::default();
472 let polling = AdaptivePolling::new(config.clone()).unwrap();
473
474 for _ in 0..15 {
476 polling.record_activity();
477 }
478
479 thread::sleep(Duration::from_secs(1));
481 polling.record_activity();
482
483 polling.adjust_interval();
484
485 let new_interval = polling.current_interval();
486 assert!(new_interval < config.default_interval);
487 }
488
489 #[test]
490 fn test_sleep_mode() {
491 let config = AdaptivePollingConfig {
492 sleep_threshold: Duration::from_millis(100),
493 enable_sleep_mode: true,
494 ..Default::default()
495 };
496 let polling = AdaptivePolling::new(config.clone()).unwrap();
497
498 thread::sleep(Duration::from_millis(150));
500 polling.adjust_interval();
501
502 assert_eq!(polling.activity_level(), ActivityLevel::Sleep);
503 assert_eq!(polling.current_interval(), config.sleep_interval);
504 }
505
506 #[test]
507 fn test_reset() {
508 let config = AdaptivePollingConfig::default();
509 let polling = AdaptivePolling::new(config.clone()).unwrap();
510
511 thread::sleep(Duration::from_millis(100));
513 polling.adjust_interval();
514
515 polling.reset();
517
518 assert_eq!(polling.current_interval(), config.default_interval);
519 assert_eq!(polling.activity_level(), ActivityLevel::Moderate);
520 }
521
522 #[test]
523 fn test_stats() {
524 let config = AdaptivePollingConfig::default();
525 let polling = AdaptivePolling::new(config).unwrap();
526
527 polling.record_activity();
528
529 let stats = polling.stats();
530 assert!(stats.time_since_activity < Duration::from_millis(100));
531 assert_eq!(stats.activity_level, ActivityLevel::Moderate);
532 }
533
534 #[test]
535 fn test_interval_bounds() {
536 let config = AdaptivePollingConfig::default();
537 let polling = AdaptivePolling::new(config.clone()).unwrap();
538
539 for _ in 0..100 {
541 polling.adjust_interval();
542 }
543
544 let interval = polling.current_interval();
545 assert!(interval >= config.min_interval);
546 assert!(interval <= config.max_interval);
547 }
548}