StaleItemMonitor.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. using InABox.Core;
  2. namespace InABox.Avalonia.Platform;
  3. public class StaleItemMonitor<T>
  4. {
  5. private readonly CoreObservableCollection<T> _items;
  6. private readonly Func<T, DateTime> _timestampSelector;
  7. private readonly Func<T, string> _displaySelector;
  8. private readonly TimeSpan _staleThreshold;
  9. private readonly TimeSpan _pollInterval;
  10. private CancellationTokenSource? _cts;
  11. private Task? _monitorTask;
  12. private readonly object _lock = new object();
  13. public event EventHandler? Changed;
  14. public StaleItemMonitor(
  15. CoreObservableCollection<T> items,
  16. Func<T, DateTime> timestampSelector,
  17. Func<T, String> displaySelector,
  18. TimeSpan staleThreshold,
  19. TimeSpan pollInterval)
  20. {
  21. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Initializing Stale Item monitor for {typeof(T).Name}");
  22. _items = items ?? throw new ArgumentNullException(nameof(items));
  23. _timestampSelector = timestampSelector ?? throw new ArgumentNullException(nameof(timestampSelector));
  24. _displaySelector = displaySelector ?? throw new ArgumentNullException(nameof(displaySelector));
  25. _staleThreshold = staleThreshold;
  26. _pollInterval = pollInterval;
  27. }
  28. public Task StartAsync()
  29. {
  30. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Starting Stale Item monitor for {typeof(T).Name}");
  31. if (_monitorTask != null && !_monitorTask.IsCompleted)
  32. {
  33. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor already started");
  34. return Task.CompletedTask; // Already running
  35. }
  36. _cts = new CancellationTokenSource();
  37. _monitorTask = Task.Run(() => MonitorLoop(_cts.Token));
  38. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor started successfully");
  39. return Task.CompletedTask;
  40. }
  41. public async Task StopAsync()
  42. {
  43. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stopping Stale Item monitor for {typeof(T).Name}");
  44. if (_monitorTask == null)
  45. {
  46. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor already stopped");
  47. return;
  48. }
  49. if (_cts == null)
  50. {
  51. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item Monitor cancellation token is not set");
  52. return;
  53. }
  54. _cts.Cancel();
  55. try
  56. {
  57. await _monitorTask;
  58. }
  59. catch (OperationCanceledException)
  60. {
  61. // Expected during cancellation
  62. }
  63. finally
  64. {
  65. _cts.Dispose();
  66. _cts = null;
  67. _monitorTask = null;
  68. }
  69. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Stale Item monitor stopped successfully");
  70. }
  71. private async Task MonitorLoop(CancellationToken token)
  72. {
  73. while (!token.IsCancellationRequested)
  74. {
  75. try
  76. {
  77. RemoveStaleItems();
  78. await Task.Delay(_pollInterval, token);
  79. }
  80. catch (OperationCanceledException)
  81. {
  82. // Cancellation requested, exit loop
  83. break;
  84. }
  85. catch (Exception ex)
  86. {
  87. // Log or handle other exceptions
  88. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Monitor error: {ex.Message}");
  89. }
  90. }
  91. }
  92. private void RemoveStaleItems()
  93. {
  94. DateTime now = DateTime.UtcNow;
  95. lock (_lock)
  96. {
  97. var stale = _items.Where(item => (now - _timestampSelector(item)) > _staleThreshold).ToArray();
  98. if (stale.Any())
  99. {
  100. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Removing Stale Items {string.Join(", ", stale.Select(x=>_displaySelector(x)))}...");
  101. _items.RemoveAll(x => stale.Contains(x)); ;
  102. Changed?.Invoke(this, EventArgs.Empty);
  103. System.Diagnostics.Debug.WriteLine($"{DateTime.Now:O} Removed {stale.Length} Stale Items.");
  104. }
  105. }
  106. }
  107. }