Skip to content

Refactor notification batching and improve thread safety in config cache#36

Merged
svtica merged 4 commits intomainfrom
claude/fix-email-notification-checkbox-y9TUz
Mar 20, 2026
Merged

Refactor notification batching and improve thread safety in config cache#36
svtica merged 4 commits intomainfrom
claude/fix-email-notification-checkbox-y9TUz

Conversation

@svtica
Copy link
Copy Markdown
Owner

@svtica svtica commented Mar 20, 2026

Summary

This PR refactors the notification system to use a batch-based approach, improves thread safety in the XML configuration cache, and removes unused code and synchronization primitives.

Key Changes

NotificationManager.vb:

  • Removed single-message properties (Messages, StartTime, Subject, HighestPriority, BatchId) and related methods (AddMessage, GetCombinedBody, ProcessEmailMessage, SendEmailAsync) in favor of a batch-based architecture using NotificationBatch class
  • Refactored QueueNotification to create batches that accumulate messages over 5 seconds before sending
  • Improved batch processing logic to:
    • Always remove batches from _activeBatches to prevent memory accumulation
    • Only enqueue emails if the cancellation token hasn't been triggered
    • Clear batch messages after processing
    • Conditionally format subject line based on message count (single vs. multiple)
  • Removed unused disposedValue field and unnecessary TaskContinuationOptions.OnlyOnRanToCompletion constraint

XMLManager.vb:

  • Added _cacheLock synchronization object to protect concurrent access to the config cache
  • Introduced InvalidateCache() public method to allow external cache invalidation (useful when GUI updates config)
  • Enhanced ReadValue() with proper locking around cache checks and updates
  • Improved cache expiry logic to clear expired entries before adding new ones, preventing unbounded cache growth
  • Protected cache operations in WriteValue() with locks

CustomScheduler.vb:

  • Removed unused disposedValue field
  • Removed unused synchronization primitives (_schedulerLock, _processingLock, _isProcessing)
  • Improved service restart process handling: now properly disposes the Process object after logging its PID, allowing the OS process to continue independently
  • Enhanced daily restart logic to re-read notification settings from XML via InvalidateCache(), ensuring the service picks up GUI-initiated configuration changes

Notable Implementation Details

  • The batch-based notification system now respects cancellation tokens while ensuring cleanup of batch resources
  • Thread-safe cache operations prevent race conditions when the GUI and service access configuration simultaneously
  • Process disposal in restart logic is now explicit and correct, releasing managed handles while allowing the OS process to persist

claude added 4 commits March 20, 2026 13:24
The service loaded the DailyRestartNotificationEnabled setting once at
startup and never re-read it. When the user unchecked the notification
checkbox in the GUI (a separate process), the service still held the
old cached value and sent the email anyway.

Fix: re-read the notification setting directly from the XML config file
(with cache invalidation) right before the notification check in
CheckDailyRestart. Also add XMLManager.InvalidateCache() so the service
bypasses stale in-memory cache entries written by the GUI process.

https://claude.ai/code/session_015AJHHxnPSut7ki1EQEp3C9
The _configCache dictionary only set its expiry when the first item was
added, but never cleared old entries on expiry. Each 5-minute cycle
added new entries on top of stale ones, causing unbounded growth over
the service's uptime.

Fix: when the cache has expired, clear all entries before adding the
new value and reset the expiry timer.

https://claude.ai/code/session_015AJHHxnPSut7ki1EQEp3C9
1. CustomScheduler.InitiateServiceRestart: wrap Process.Start in Using
   to release the managed handle immediately. The OS process continues
   running independently — Dispose only frees the .NET wrapper.

2. NotificationManager: change ContinueWith from OnlyOnRanToCompletion
   to unconditional so _activeBatches entries are always removed, even
   when the cancellation token fires. Also clear batch.Messages after
   processing to release EmailMessage references sooner.

3. NotificationManager: remove unused Messages, StartTime, Subject,
   HighestPriority, BatchId properties and AddMessage/GetCombinedBody
   methods that duplicated NotificationBatch and were never called —
   the Messages list in particular grew unbounded.

https://claude.ai/code/session_015AJHHxnPSut7ki1EQEp3C9
XMLManager: protect _configCache with SyncLock in InvalidateCache,
ReadValue, and WriteValue to prevent races between the scheduler
thread and concurrent reads.

Remove dead code:
- NotificationManager: unused ProcessEmailMessage (superseded by
  StartEmailProcessor), unused SendEmailAsync (never called),
  unused disposedValue field (duplicate of _disposed)
- CustomScheduler: unused disposedValue, _isProcessing, _processingLock
  and _schedulerLock (declared/disposed but never acquired)

https://claude.ai/code/session_015AJHHxnPSut7ki1EQEp3C9
@svtica svtica merged commit a116acc into main Mar 20, 2026
1 check passed
@svtica svtica deleted the claude/fix-email-notification-checkbox-y9TUz branch March 20, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants