1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::sync::{Arc, Mutex};
11use std::time::Duration;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
15pub enum TaskType {
16 CpuIntensive,
18 IoIntensive,
20 NetworkIntensive,
22 MemoryIntensive,
24 GpuCompute,
26 #[default]
28 Mixed,
29 Streaming,
31 Background,
33}
34
35impl TaskType {
36 pub fn description(&self) -> &'static str {
38 match self {
39 Self::CpuIntensive => "CPU-intensive workload",
40 Self::IoIntensive => "IO-intensive workload",
41 Self::NetworkIntensive => "Network-intensive workload",
42 Self::MemoryIntensive => "Memory-intensive workload",
43 Self::GpuCompute => "GPU compute workload",
44 Self::Mixed => "Mixed workload",
45 Self::Streaming => "Streaming workload",
46 Self::Background => "Background task",
47 }
48 }
49
50 pub fn resource_priority(&self) -> (f64, f64, f64, f64) {
52 match self {
53 Self::CpuIntensive => (1.0, 0.3, 0.2, 0.1),
54 Self::IoIntensive => (0.3, 1.0, 0.2, 0.1),
55 Self::NetworkIntensive => (0.3, 0.2, 1.0, 0.1),
56 Self::MemoryIntensive => (0.2, 0.3, 0.2, 1.0),
57 Self::GpuCompute => (0.5, 0.2, 0.1, 0.8),
58 Self::Mixed => (0.5, 0.5, 0.5, 0.5),
59 Self::Streaming => (0.4, 0.4, 0.6, 0.4),
60 Self::Background => (0.2, 0.2, 0.2, 0.2),
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct TaskMemoryProfile {
68 pub task_id: u64,
70 pub tokio_task_id: Option<u64>,
72 pub task_name: String,
74 pub task_type: TaskType,
76 pub created_at_ms: u64,
78 pub completed_at_ms: Option<u64>,
80 pub total_bytes: u64,
82 pub current_memory: u64,
84 pub peak_memory: u64,
86 pub total_allocations: u64,
88 pub total_deallocations: u64,
90 pub duration_ns: u64,
92 pub allocation_rate: f64,
94 pub efficiency_score: f64,
96 pub average_allocation_size: f64,
98 #[serde(default)]
104 pub thread_id_u64: Option<u64>,
105}
106
107impl TaskMemoryProfile {
108 pub fn new(task_id: u64, task_name: String, task_type: TaskType) -> Self {
110 Self {
111 task_id,
112 tokio_task_id: None,
113 task_name,
114 task_type,
115 created_at_ms: Self::now_ms(),
116 completed_at_ms: None,
117 total_bytes: 0,
118 current_memory: 0,
119 peak_memory: 0,
120 total_allocations: 0,
121 total_deallocations: 0,
122 duration_ns: 0,
123 allocation_rate: 0.0,
124 efficiency_score: 1.0,
125 average_allocation_size: 0.0,
126 thread_id_u64: None,
127 }
128 }
129
130 pub fn set_thread_id_u64(&mut self, thread_id: Option<u64>) {
132 self.thread_id_u64 = thread_id;
133 }
134
135 pub fn with_tokio_id(
137 task_id: u64,
138 tokio_task_id: u64,
139 task_name: String,
140 task_type: TaskType,
141 ) -> Self {
142 let mut profile = Self::new(task_id, task_name, task_type);
143 profile.tokio_task_id = Some(tokio_task_id);
144 profile
145 }
146
147 fn now_ms() -> u64 {
149 std::time::SystemTime::now()
150 .duration_since(std::time::UNIX_EPOCH)
151 .unwrap_or_default()
152 .as_millis() as u64
153 }
154
155 pub fn mark_completed(&mut self) {
157 self.completed_at_ms.get_or_insert_with(Self::now_ms);
158 self.update_metrics();
159 }
160
161 pub fn record_allocation(&mut self, size: u64) {
163 self.total_bytes += size;
164 self.current_memory += size;
165 self.total_allocations += 1;
166
167 if self.current_memory > self.peak_memory {
168 self.peak_memory = self.current_memory;
169 }
170
171 self.update_metrics();
172 }
173
174 pub fn record_reallocation(&mut self, old_size: u64, new_size: u64) {
179 self.total_bytes += new_size;
180 self.current_memory = self
181 .current_memory
182 .saturating_sub(old_size)
183 .saturating_add(new_size);
184 if self.current_memory > self.peak_memory {
185 self.peak_memory = self.current_memory;
186 }
187 self.update_metrics();
188 }
189
190 pub fn record_deallocation(&mut self, size: u64) {
192 self.current_memory = self.current_memory.saturating_sub(size);
193 self.total_deallocations += 1;
194 self.update_metrics();
195 }
196
197 pub fn is_completed(&self) -> bool {
199 self.completed_at_ms.is_some()
200 }
201
202 pub fn lifetime(&self) -> Duration {
204 let end_ms = self.completed_at_ms.unwrap_or_else(Self::now_ms);
205 let start_ms = self.created_at_ms;
206 Duration::from_millis(end_ms.saturating_sub(start_ms))
207 }
208
209 pub fn memory_efficiency(&self) -> f64 {
211 if self.total_bytes == 0 {
212 1.0
213 } else {
214 let deallocated = self.total_bytes - self.current_memory;
215 deallocated as f64 / self.total_bytes as f64
216 }
217 }
218
219 pub fn has_potential_leak(&self) -> bool {
221 self.is_completed() && self.current_memory > 0
222 }
223
224 fn update_metrics(&mut self) {
226 self.refresh_at(Self::now_ms().saturating_mul(1_000_000));
227 }
228
229 pub(crate) fn refresh_at(&mut self, captured_at_ns: u64) {
230 let end = self
231 .completed_at_ms
232 .map(|ms| ms.saturating_mul(1_000_000))
233 .unwrap_or(captured_at_ns);
234 self.duration_ns = end.saturating_sub(self.created_at_ms.saturating_mul(1_000_000));
235 let lifetime_secs = self.duration_ns as f64 / 1_000_000_000.0;
236
237 self.allocation_rate = if lifetime_secs > 0.0 {
238 self.total_bytes as f64 / lifetime_secs
239 } else {
240 0.0
241 };
242
243 self.efficiency_score = self.memory_efficiency();
244
245 self.average_allocation_size = if self.total_allocations > 0 {
246 self.total_bytes as f64 / self.total_allocations as f64
247 } else {
248 0.0
249 };
250 }
251
252 pub fn summary(&self) -> String {
254 format!(
255 "Task '{}' (ID: {}, Type: {:?}): {} allocations, {:.2} MB total, {:.2} MB peak, {:.1}% efficiency",
256 self.task_name,
257 self.task_id,
258 self.task_type,
259 self.total_allocations,
260 self.total_bytes as f64 / 1_048_576.0,
261 self.peak_memory as f64 / 1_048_576.0,
262 self.efficiency_score * 100.0
263 )
264 }
265}
266
267#[derive(Debug, Clone)]
269pub struct TaskProfileManager {
270 profiles: Arc<Mutex<HashMap<u64, TaskMemoryProfile>>>,
271 next_task_id: Arc<AtomicU64>,
272}
273
274impl TaskProfileManager {
275 pub fn new() -> Self {
277 Self {
278 profiles: Arc::new(Mutex::new(HashMap::new())),
279 next_task_id: Arc::new(AtomicU64::new(1)),
280 }
281 }
282
283 pub fn create_task(&self, task_name: String, task_type: TaskType) -> u64 {
285 let task_id = self.next_task_id.fetch_add(1, Ordering::Relaxed);
286
287 let profile = TaskMemoryProfile::new(task_id, task_name, task_type);
288
289 if let Ok(mut profiles) = self.profiles.lock() {
290 profiles.insert(task_id, profile);
291 }
292
293 task_id
294 }
295
296 pub fn record_allocation(&self, task_id: u64, size: u64) {
298 if let Ok(mut profiles) = self.profiles.lock() {
299 if let Some(profile) = profiles.get_mut(&task_id) {
300 profile.record_allocation(size);
301 }
302 }
303 }
304
305 pub fn record_deallocation(&self, task_id: u64, size: u64) {
307 if let Ok(mut profiles) = self.profiles.lock() {
308 if let Some(profile) = profiles.get_mut(&task_id) {
309 profile.record_deallocation(size);
310 }
311 }
312 }
313
314 pub fn complete_task(&self, task_id: u64) {
316 if let Ok(mut profiles) = self.profiles.lock() {
317 if let Some(profile) = profiles.get_mut(&task_id) {
318 profile.mark_completed();
319 }
320 }
321 }
322
323 pub fn get_profile(&self, task_id: u64) -> Option<TaskMemoryProfile> {
325 if let Ok(profiles) = self.profiles.lock() {
326 profiles.get(&task_id).cloned()
327 } else {
328 None
329 }
330 }
331
332 pub fn get_all_profiles(&self) -> Vec<TaskMemoryProfile> {
334 if let Ok(profiles) = self.profiles.lock() {
335 profiles.values().cloned().collect()
336 } else {
337 Vec::new()
338 }
339 }
340
341 pub fn get_profiles_by_type(&self, task_type: TaskType) -> Vec<TaskMemoryProfile> {
343 if let Ok(profiles) = self.profiles.lock() {
344 profiles
345 .values()
346 .filter(|p| p.task_type == task_type)
347 .cloned()
348 .collect()
349 } else {
350 Vec::new()
351 }
352 }
353
354 pub fn get_aggregated_stats(&self) -> AggregatedTaskStats {
356 let profiles = self.get_all_profiles();
357
358 let total_tasks = profiles.len();
359 let completed_tasks = profiles.iter().filter(|p| p.is_completed()).count();
360
361 let total_memory_allocated: u64 = profiles.iter().map(|p| p.total_bytes).sum();
362 let current_memory_usage: u64 = profiles.iter().map(|p| p.current_memory).sum();
363 let peak_memory_usage: u64 = profiles.iter().map(|p| p.peak_memory).max().unwrap_or(0);
364
365 let total_duration: Duration = profiles.iter().map(|p| p.lifetime()).sum::<Duration>();
366 let average_lifetime = if total_tasks > 0 {
367 let total_secs = total_duration.as_secs_f64();
368 let avg_secs = total_secs / total_tasks as f64;
369 Duration::from_secs_f64(avg_secs)
370 } else {
371 Duration::ZERO
372 };
373
374 let overall_efficiency = if total_memory_allocated > 0 {
375 let total_deallocated = total_memory_allocated - current_memory_usage;
376 total_deallocated as f64 / total_memory_allocated as f64
377 } else {
378 1.0
379 };
380
381 let potential_leaks = profiles.iter().filter(|p| p.has_potential_leak()).count();
382
383 AggregatedTaskStats {
384 total_tasks,
385 completed_tasks,
386 total_memory_allocated,
387 current_memory_usage,
388 peak_memory_usage,
389 average_lifetime,
390 overall_efficiency,
391 potential_leaks,
392 }
393 }
394
395 pub fn clear(&self) {
397 if let Ok(mut profiles) = self.profiles.lock() {
398 profiles.clear();
399 }
400 }
401
402 pub fn active_task_count(&self) -> usize {
404 if let Ok(profiles) = self.profiles.lock() {
405 profiles.iter().filter(|(_, p)| !p.is_completed()).count()
406 } else {
407 0
408 }
409 }
410}
411
412impl Default for TaskProfileManager {
413 fn default() -> Self {
414 Self::new()
415 }
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
420pub struct AggregatedTaskStats {
421 pub total_tasks: usize,
423 pub completed_tasks: usize,
425 pub total_memory_allocated: u64,
427 pub current_memory_usage: u64,
429 pub peak_memory_usage: u64,
431 pub average_lifetime: Duration,
433 pub overall_efficiency: f64,
435 pub potential_leaks: usize,
437}
438
439impl AggregatedTaskStats {
440 pub fn new() -> Self {
442 Self {
443 total_tasks: 0,
444 completed_tasks: 0,
445 total_memory_allocated: 0,
446 current_memory_usage: 0,
447 peak_memory_usage: 0,
448 average_lifetime: Duration::ZERO,
449 overall_efficiency: 1.0,
450 potential_leaks: 0,
451 }
452 }
453
454 pub fn memory_summary(&self) -> String {
456 format!(
457 "Tasks: {} ({}% complete), Memory: {:.2}MB allocated, {:.2}MB current, {:.1}% efficiency, {} potential leaks",
458 self.total_tasks,
459 self.completed_tasks
460 .checked_mul(100)
461 .and_then(|v| v.checked_div(self.total_tasks))
462 .unwrap_or(0),
463 self.total_memory_allocated as f64 / 1_048_576.0,
464 self.current_memory_usage as f64 / 1_048_576.0,
465 self.overall_efficiency * 100.0,
466 self.potential_leaks
467 )
468 }
469}
470
471impl Default for AggregatedTaskStats {
472 fn default() -> Self {
473 Self::new()
474 }
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480
481 #[test]
482 fn test_task_type_description() {
483 assert_eq!(
484 TaskType::CpuIntensive.description(),
485 "CPU-intensive workload"
486 );
487 assert_eq!(TaskType::IoIntensive.description(), "IO-intensive workload");
488 assert_eq!(
489 TaskType::NetworkIntensive.description(),
490 "Network-intensive workload"
491 );
492 }
493
494 #[test]
495 fn test_task_type_resource_priority() {
496 let (cpu, io, net, mem) = TaskType::CpuIntensive.resource_priority();
497 assert!(cpu > io && cpu > net && cpu > mem);
498
499 let (cpu, io, net, mem) = TaskType::IoIntensive.resource_priority();
500 assert!(io > cpu && io > net && io > mem);
501 }
502
503 #[test]
504 fn test_task_memory_profile_basic() {
505 let profile = TaskMemoryProfile::new(1, "test_task".to_string(), TaskType::CpuIntensive);
506
507 assert_eq!(profile.task_id, 1);
508 assert_eq!(profile.task_name, "test_task");
509 assert_eq!(profile.task_type, TaskType::CpuIntensive);
510 assert!(!profile.is_completed());
511 assert_eq!(profile.current_memory, 0);
512 assert_eq!(profile.total_bytes, 0);
513 }
514
515 #[test]
516 fn test_record_allocation() {
517 let mut profile = TaskMemoryProfile::new(1, "test".to_string(), TaskType::Mixed);
518
519 profile.record_allocation(1024);
520 assert_eq!(profile.current_memory, 1024);
521 assert_eq!(profile.total_bytes, 1024);
522 assert_eq!(profile.peak_memory, 1024);
523 assert_eq!(profile.total_allocations, 1);
524
525 profile.record_allocation(2048);
526 assert_eq!(profile.current_memory, 3072);
527 assert_eq!(profile.total_bytes, 3072);
528 assert_eq!(profile.peak_memory, 3072);
529 assert_eq!(profile.total_allocations, 2);
530 }
531
532 #[test]
533 fn test_record_deallocation() {
534 let mut profile = TaskMemoryProfile::new(1, "test".to_string(), TaskType::Mixed);
535
536 profile.record_allocation(3072);
537 profile.record_deallocation(1024);
538 assert_eq!(profile.current_memory, 2048);
539 assert_eq!(profile.total_bytes, 3072);
540 assert_eq!(profile.peak_memory, 3072);
541 assert_eq!(profile.total_deallocations, 1);
542 }
543
544 #[test]
545 fn test_memory_efficiency() {
546 let mut profile = TaskMemoryProfile::new(1, "test".to_string(), TaskType::Mixed);
547
548 profile.record_allocation(1000);
549 profile.record_deallocation(1000);
550 assert_eq!(profile.memory_efficiency(), 1.0);
551
552 profile.record_allocation(1000);
553 assert_eq!(profile.memory_efficiency(), 0.5);
554 }
555
556 #[test]
557 fn test_potential_leak_detection() {
558 let mut profile = TaskMemoryProfile::new(1, "test".to_string(), TaskType::Mixed);
559
560 profile.record_allocation(1000);
561 assert!(!profile.has_potential_leak());
562
563 profile.record_deallocation(1000);
564 profile.mark_completed();
565 assert!(!profile.has_potential_leak());
566
567 let mut profile2 = TaskMemoryProfile::new(2, "test2".to_string(), TaskType::Mixed);
568 profile2.record_allocation(1000);
569 profile2.mark_completed();
570 assert!(profile2.has_potential_leak());
571 }
572
573 #[test]
574 fn test_task_profile_manager() {
575 let manager = TaskProfileManager::new();
576
577 let task_id = manager.create_task("test_task".to_string(), TaskType::CpuIntensive);
578 assert!(task_id > 0);
579
580 manager.record_allocation(task_id, 1024);
581 manager.record_allocation(task_id, 2048);
582
583 let profile = manager.get_profile(task_id);
584 assert!(profile.is_some());
585 assert_eq!(profile.unwrap().total_bytes, 3072);
586 }
587
588 #[test]
589 fn test_aggregated_stats() {
590 let manager = TaskProfileManager::new();
591
592 let task1 = manager.create_task("task1".to_string(), TaskType::Mixed);
593 manager.record_allocation(task1, 1000);
594 manager.record_deallocation(task1, 500);
595 manager.complete_task(task1);
596
597 let task2 = manager.create_task("task2".to_string(), TaskType::Mixed);
598 manager.record_allocation(task2, 2000);
599
600 let stats = manager.get_aggregated_stats();
601 assert_eq!(stats.total_tasks, 2);
602 assert_eq!(stats.completed_tasks, 1);
603 assert_eq!(stats.total_memory_allocated, 3000);
604 assert_eq!(stats.current_memory_usage, 2500);
605 }
606
607 #[test]
608 fn test_active_task_count() {
609 let manager = TaskProfileManager::new();
610
611 let task1 = manager.create_task("task1".to_string(), TaskType::Mixed);
612 let task2 = manager.create_task("task2".to_string(), TaskType::Mixed);
613
614 assert_eq!(manager.active_task_count(), 2);
615
616 manager.complete_task(task1);
617 assert_eq!(manager.active_task_count(), 1);
618
619 manager.complete_task(task2);
620 assert_eq!(manager.active_task_count(), 0);
621 }
622
623 #[test]
624 fn test_profiles_by_type() {
625 let manager = TaskProfileManager::new();
626
627 let _ = manager.create_task("cpu_task".to_string(), TaskType::CpuIntensive);
628 let _ = manager.create_task("io_task".to_string(), TaskType::IoIntensive);
629 let _ = manager.create_task("cpu_task2".to_string(), TaskType::CpuIntensive);
630
631 let cpu_profiles = manager.get_profiles_by_type(TaskType::CpuIntensive);
632 assert_eq!(cpu_profiles.len(), 2);
633
634 let io_profiles = manager.get_profiles_by_type(TaskType::IoIntensive);
635 assert_eq!(io_profiles.len(), 1);
636 }
637}