1use crate::unified::backend::{AsyncRuntimeType, BackendError, RuntimeEnvironment};
6use std::sync::atomic::AtomicUsize;
7use std::sync::Arc;
8use tracing::{debug, info, warn};
9
10pub struct EnvironmentDetector {
13 config: DetectionConfig,
15 runtime_stats: RuntimeStatistics,
17}
18
19#[derive(Debug, Clone)]
21pub struct DetectionConfig {
22 pub deep_async_detection: bool,
24 pub analysis_period_ms: u64,
26 pub multi_thread_threshold: usize,
28 pub max_detection_time_ms: u64,
30}
31
32#[derive(Debug, Clone)]
34pub struct RuntimeStatistics {
35 pub active_threads: Arc<AtomicUsize>,
37 pub async_tasks: Arc<AtomicUsize>,
39 pub peak_thread_count: usize,
41 pub detection_duration_ms: u64,
43}
44
45#[derive(Debug, Clone)]
47pub struct EnvironmentAnalysis {
48 pub environment: RuntimeEnvironment,
50 pub confidence: f64,
52 pub alternatives: Vec<RuntimeEnvironment>,
54 pub detection_metadata: DetectionMetadata,
56}
57
58#[derive(Debug, Clone)]
60pub struct DetectionMetadata {
61 pub detection_time_ms: u64,
63 pub sample_count: usize,
65 pub method: DetectionMethod,
67 pub warnings: Vec<String>,
69}
70
71#[derive(Debug, Clone, PartialEq)]
73pub enum DetectionMethod {
74 Static,
76 Dynamic,
78 Hybrid,
80 Manual,
82}
83
84impl Default for DetectionConfig {
85 fn default() -> Self {
87 Self {
88 deep_async_detection: true,
89 analysis_period_ms: 100,
90 multi_thread_threshold: 2,
91 max_detection_time_ms: 500,
92 }
93 }
94}
95
96impl Default for RuntimeStatistics {
97 fn default() -> Self {
99 Self {
100 active_threads: Arc::new(AtomicUsize::new(1)),
101 async_tasks: Arc::new(AtomicUsize::new(0)),
102 peak_thread_count: 1,
103 detection_duration_ms: 0,
104 }
105 }
106}
107
108impl EnvironmentDetector {
109 pub fn new(config: DetectionConfig) -> Self {
111 debug!("Creating environment detector with config: {:?}", config);
112
113 Self {
114 config,
115 runtime_stats: RuntimeStatistics::default(),
116 }
117 }
118
119 pub fn analyze_environment(&mut self) -> Result<EnvironmentAnalysis, BackendError> {
122 let start_time = std::time::Instant::now();
123 info!("Starting comprehensive environment analysis");
124
125 let mut warnings = Vec::new();
126
127 let static_env = self.perform_static_analysis(&mut warnings)?;
129 debug!("Static analysis result: {:?}", static_env);
130
131 let dynamic_env = if self.config.deep_async_detection {
133 Some(self.perform_dynamic_analysis(&mut warnings)?)
134 } else {
135 None
136 };
137
138 let has_dynamic = dynamic_env.is_some();
140 let (final_env, confidence, alternatives) =
141 self.synthesize_results(static_env, dynamic_env, &warnings)?;
142
143 let detection_time = start_time.elapsed().as_millis() as u64;
144 self.runtime_stats.detection_duration_ms = detection_time;
145
146 let analysis = EnvironmentAnalysis {
147 environment: final_env,
148 confidence,
149 alternatives,
150 detection_metadata: DetectionMetadata {
151 detection_time_ms: detection_time,
152 sample_count: self.calculate_sample_count(),
153 method: self.determine_detection_method(has_dynamic),
154 warnings,
155 },
156 };
157
158 info!(
159 "Environment analysis completed: {:?} (confidence: {:.2})",
160 analysis.environment, analysis.confidence
161 );
162
163 Ok(analysis)
164 }
165
166 fn perform_static_analysis(
168 &self,
169 warnings: &mut Vec<String>,
170 ) -> Result<RuntimeEnvironment, BackendError> {
171 debug!("Performing static environment analysis");
172
173 let logical_cores = std::thread::available_parallelism()
175 .map(|p| p.get())
176 .unwrap_or_else(|e| {
177 warnings.push(format!("Could not detect CPU cores: {}", e));
178 1
179 });
180
181 debug!("Detected {} logical CPU cores", logical_cores);
182
183 let async_runtime = self.detect_async_runtime_static(warnings);
185
186 let environment = match (async_runtime, logical_cores) {
188 (Some(runtime_type), 0) => {
189 warnings.push("Zero cores detected with async runtime".to_string());
190 RuntimeEnvironment::AsyncRuntime { runtime_type }
191 }
192 (Some(runtime_type), 1) => RuntimeEnvironment::AsyncRuntime { runtime_type },
193 (Some(_runtime_type), cores) if cores >= self.config.multi_thread_threshold => {
194 RuntimeEnvironment::Hybrid {
195 thread_count: cores,
196 async_task_count: 0, }
198 }
199 (Some(runtime_type), _cores) => {
200 RuntimeEnvironment::AsyncRuntime { runtime_type }
202 }
203 (None, 1) => RuntimeEnvironment::SingleThreaded,
204 (None, cores) if cores >= self.config.multi_thread_threshold => {
205 RuntimeEnvironment::MultiThreaded {
206 thread_count: cores,
207 }
208 }
209 (None, cores) => {
210 warnings.push(format!("Low core count {} but above single-thread", cores));
211 RuntimeEnvironment::SingleThreaded
212 }
213 };
214
215 debug!("Static analysis determined environment: {:?}", environment);
216 Ok(environment)
217 }
218
219 fn detect_async_runtime_static(&self, warnings: &mut Vec<String>) -> Option<AsyncRuntimeType> {
221 debug!("Detecting async runtime using static analysis");
222
223 if self.is_tokio_runtime_present() {
225 debug!("Tokio runtime detected via static analysis");
226 return Some(AsyncRuntimeType::Tokio);
227 }
228
229 if self.is_async_std_runtime_present() {
231 debug!("async-std runtime detected via static analysis");
232 return Some(AsyncRuntimeType::AsyncStd);
233 }
234
235 if let Ok(async_env) = std::env::var("ASYNC_RUNTIME") {
237 match async_env.to_lowercase().as_str() {
238 "tokio" => {
239 debug!("Tokio runtime detected via environment variable");
240 return Some(AsyncRuntimeType::Tokio);
241 }
242 "async-std" => {
243 debug!("async-std runtime detected via environment variable");
244 return Some(AsyncRuntimeType::AsyncStd);
245 }
246 other => {
247 warnings.push(format!("Unknown async runtime specified: {}", other));
248 return Some(AsyncRuntimeType::Custom);
249 }
250 }
251 }
252
253 debug!("No async runtime detected in static analysis");
254 None
255 }
256
257 fn is_tokio_runtime_present(&self) -> bool {
259 if std::env::var("TOKIO_WORKER_THREADS").is_ok() {
261 return true;
262 }
263
264 false
268 }
269
270 fn is_async_std_runtime_present(&self) -> bool {
272 if std::env::var("ASYNC_STD_THREAD_COUNT").is_ok() {
277 return true;
278 }
279
280 false
284 }
285
286 fn perform_dynamic_analysis(
288 &mut self,
289 warnings: &mut Vec<String>,
290 ) -> Result<RuntimeEnvironment, BackendError> {
291 debug!("Performing dynamic runtime analysis");
292
293 let analysis_start = std::time::Instant::now();
294 let max_duration = std::time::Duration::from_millis(self.config.max_detection_time_ms);
295
296 let mut sample_count = 0;
298 let mut thread_samples = Vec::new();
299 let mut async_indicators = Vec::new();
300
301 while analysis_start.elapsed() < max_duration {
302 let current_threads = self.sample_thread_activity();
304 thread_samples.push(current_threads);
305
306 let async_activity = self.sample_async_activity();
308 async_indicators.push(async_activity);
309
310 sample_count += 1;
311
312 std::thread::sleep(std::time::Duration::from_millis(
314 self.config.analysis_period_ms / 10,
315 ));
316 }
317
318 let avg_threads = if thread_samples.is_empty() {
320 1
321 } else {
322 thread_samples.iter().sum::<usize>() / thread_samples.len()
323 };
324
325 let peak_threads = thread_samples.into_iter().max().unwrap_or(1);
326 self.runtime_stats.peak_thread_count = peak_threads;
327
328 let has_async_activity = async_indicators.iter().any(|&active| active);
329
330 debug!(
331 "Dynamic analysis: avg_threads={}, peak_threads={}, async_activity={}",
332 avg_threads, peak_threads, has_async_activity
333 );
334
335 let environment = match (has_async_activity, peak_threads) {
337 (true, 0) => {
338 warnings.push("Async activity detected with zero threads".to_string());
339 let runtime_type = self
340 .detect_async_runtime_static(warnings)
341 .unwrap_or(AsyncRuntimeType::Custom);
342 RuntimeEnvironment::AsyncRuntime { runtime_type }
343 }
344 (true, 1) => {
345 let runtime_type = self
347 .detect_async_runtime_static(warnings)
348 .unwrap_or(AsyncRuntimeType::Custom);
349 RuntimeEnvironment::AsyncRuntime { runtime_type }
350 }
351 (true, threads) => {
352 RuntimeEnvironment::Hybrid {
354 thread_count: threads,
355 async_task_count: sample_count, }
357 }
358 (false, 1) => RuntimeEnvironment::SingleThreaded,
359 (false, threads) => RuntimeEnvironment::MultiThreaded {
360 thread_count: threads,
361 },
362 };
363
364 debug!("Dynamic analysis determined environment: {:?}", environment);
365 Ok(environment)
366 }
367
368 fn sample_thread_activity(&self) -> usize {
370 std::thread::available_parallelism()
375 .map(|p| p.get())
376 .unwrap_or(1)
377 }
378
379 fn sample_async_activity(&self) -> bool {
381 self.is_tokio_runtime_present() || self.is_async_std_runtime_present()
386 }
387
388 fn synthesize_results(
390 &self,
391 static_result: RuntimeEnvironment,
392 dynamic_result: Option<RuntimeEnvironment>,
393 warnings: &[String],
394 ) -> Result<(RuntimeEnvironment, f64, Vec<RuntimeEnvironment>), BackendError> {
395 debug!("Synthesizing analysis results");
396
397 let mut alternatives = Vec::new();
398 let _base_confidence = 0.7; let (final_environment, mut confidence) = match dynamic_result {
401 Some(dynamic_env) => {
402 if std::mem::discriminant(&static_result) == std::mem::discriminant(&dynamic_env) {
404 let confidence = 0.95;
406 alternatives.push(static_result);
407 (dynamic_env, confidence)
408 } else {
409 let confidence = 0.75;
411 alternatives.push(static_result);
412 (dynamic_env, confidence)
413 }
414 }
415 None => {
416 let confidence = 0.80;
418 (static_result, confidence)
419 }
420 };
421
422 if !warnings.is_empty() {
424 confidence -= 0.1 * warnings.len() as f64;
425 confidence = confidence.max(0.3); }
427
428 debug!(
429 "Final synthesis: {:?} with confidence {:.2}",
430 final_environment, confidence
431 );
432 Ok((final_environment, confidence, alternatives))
433 }
434
435 fn calculate_sample_count(&self) -> usize {
437 let samples = self.runtime_stats.detection_duration_ms / self.config.analysis_period_ms;
440 samples.max(1) as usize
441 }
442
443 fn determine_detection_method(&self, used_dynamic: bool) -> DetectionMethod {
445 if used_dynamic {
446 DetectionMethod::Hybrid
447 } else {
448 DetectionMethod::Static
449 }
450 }
451
452 pub fn runtime_statistics(&self) -> &RuntimeStatistics {
454 &self.runtime_stats
455 }
456}
457
458pub fn detect_environment() -> Result<RuntimeEnvironment, BackendError> {
461 let mut detector = EnvironmentDetector::new(DetectionConfig::default());
462 let analysis = detector.analyze_environment()?;
463
464 if analysis.confidence < 0.5 {
465 warn!(
466 "Low confidence environment detection: {:.2}",
467 analysis.confidence
468 );
469 }
470
471 Ok(analysis.environment)
472}
473
474pub fn detect_environment_detailed(
477 config: DetectionConfig,
478) -> Result<EnvironmentAnalysis, BackendError> {
479 let mut detector = EnvironmentDetector::new(config);
480 detector.analyze_environment()
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[test]
488 fn test_detector_creation() {
489 let config = DetectionConfig::default();
490 let detector = EnvironmentDetector::new(config);
491 assert_eq!(detector.runtime_stats.peak_thread_count, 1);
492 }
493
494 #[test]
495 fn test_static_analysis() {
496 let config = DetectionConfig::default();
497 let detector = EnvironmentDetector::new(config);
498 let mut warnings = Vec::new();
499
500 let result = detector.perform_static_analysis(&mut warnings);
501 assert!(result.is_ok());
502 }
503
504 #[test]
505 fn test_tokio_detection() {
506 let config = DetectionConfig::default();
507 let detector = EnvironmentDetector::new(config);
508
509 let _has_tokio = detector.is_tokio_runtime_present();
511 }
513
514 #[test]
515 fn test_environment_analysis_confidence() {
516 let mut detector = EnvironmentDetector::new(DetectionConfig::default());
517 let analysis = detector.analyze_environment();
518
519 assert!(analysis.is_ok());
520 let analysis = analysis.unwrap();
521 assert!(analysis.confidence >= 0.0 && analysis.confidence <= 1.0);
522 }
523
524 #[test]
525 fn test_quick_detection() {
526 let result = detect_environment();
527 assert!(result.is_ok());
528 }
529
530 #[test]
531 fn test_detailed_detection() {
532 let config = DetectionConfig {
533 deep_async_detection: false, max_detection_time_ms: 50, ..Default::default()
536 };
537
538 let result = detect_environment_detailed(config);
539 assert!(result.is_ok());
540
541 let analysis = result.unwrap();
542 assert!(analysis.detection_metadata.detection_time_ms <= 100); }
544}