Skip to content

chore: code quality pass — modernize patterns, fix docs, add tests (QUAL-007)#468

Merged
laurentiu021 merged 1 commit into
mainfrom
chore/quality-pass
May 20, 2026
Merged

chore: code quality pass — modernize patterns, fix docs, add tests (QUAL-007)#468
laurentiu021 merged 1 commit into
mainfrom
chore/quality-pass

Conversation

@laurentiu021

@laurentiu021 laurentiu021 commented May 20, 2026

Copy link
Copy Markdown
Owner

Summary

Comprehensive quality pass addressing audit findings:

Fixes

  • SECURITY.md — supported versions table now shows 1.0.x (was 0.49.x)
  • README.md — .NET badge updated to 10.0 (was 9.0)
  • CONTRIBUTING.md — .NET SDK reference updated to 10
  • ARCHITECTURE.md — .NET/C# version references updated
  • SysManager.csproj — Version/FileVersion/AssemblyVersion updated to 1.0.0
  • AboutView.xaml.cs — added missing License header line
  • NetworkRepairService — now implements IDisposable (disposes SemaphoreSlim)
  • SpeedTestHistoryService — now implements IDisposable (disposes SemaphoreSlim)
  • Test isolation — added CollectionDefinition for OperationLock singleton tests

Modernization (62 files)

  • Sealed all 27 ViewModels (none are subclassed, improves JIT devirtualization)
  • Replaced ~70 old-style null checks with pattern matching (is null / is not null) in Services
  • Replaced 24 new List<T>() with [] collection expressions in Services

New Tests

  • BulkObservableCollectionTests — 6 tests (ReplaceWith behavior, notifications, edge cases)
  • WingetTableParserTests — 6 tests (empty input, no header, valid table, short lines, summary stop)

Test plan

  • CI build passes (all projects: 0 warnings, 0 errors)
  • Unit tests pass (12 new tests + existing 2200+)
  • No behavioral changes — only syntactic modernization + bug fixes

Summary by CodeRabbit

  • Documentation

    • Updated to .NET 10 SDK and C# 14.
    • Version bumped to 1.0.0.
    • Updated security policy to support 1.0.x versions.
  • New Features

    • Added resource disposal support to NetworkRepairService and SpeedTestHistoryService.
  • Tests

    • Added test suites for bulk collection operations and command-line table parsing.
    • Updated test parallelization configuration for shared resource tests.
  • Chores

    • Modernized codebase with pattern matching and collection expressions.
    • Made view models sealed to prevent unintended inheritance.

Review Change Stack

…UAL-007)

- Seal all 27 ViewModels (none are subclassed)
- Modernize null checks to pattern matching (is null / is not null) in 16 services
- Replace new List<T>() with collection expressions in 14 services
- Fix IDisposable: NetworkRepairService, SpeedTestHistoryService now dispose SemaphoreSlim
- Fix test isolation: add CollectionDefinition for OperationLock singleton tests
- Add BulkObservableCollectionTests (6 tests) and WingetTableParserTests (6 tests)
- Fix docs: README badge, CONTRIBUTING, ARCHITECTURE, SECURITY versions → .NET 10 / v1.0.0
- Fix SysManager.csproj Version/FileVersion/AssemblyVersion → 1.0.0
- Fix AboutView.xaml.cs missing License header line
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Comprehensive modernization passing: framework upgrade to .NET 10/C# 14, version bump to 1.0.0, security policy update to 1.0.x support, widespread service code refactoring applying pattern matching and collection expressions, new test suites, IDisposable resource management, and 24 sealed ViewModels.

Changes

Framework and Release Modernization

Layer / File(s) Summary
Documentation and version updates
ARCHITECTURE.md, CONTRIBUTING.md, README.md, SECURITY.md, SysManager/SysManager/SysManager.csproj
Tech stack documentation updated from .NET 9/C# 13 to .NET 10/C# 14; security policy updated to mark version 1.0.x as supported and <1.0 as unsupported; project assembly and package versions bumped from 0.48.21 to 1.0.0.

Test Infrastructure and New Test Coverage

Layer / File(s) Summary
Test infrastructure and test suites
SysManager/SysManager.Tests/TestCollections.cs, SysManager/SysManager.Tests/OperationLockServiceTests.cs, SysManager/SysManager.Tests/BulkObservableCollectionTests.cs, SysManager/SysManager.Tests/WingetTableParserTests.cs
Added xUnit test collection definition OperationLockCollection with parallel execution disabled for sequential OperationLockService singleton tests; new BulkObservableCollectionTests covering ReplaceWith behavior (clearing, repopulating, reset notifications, null/empty handling); new WingetTableParserTests verifying table parsing with header/summary pattern matching, empty input handling, robustness to short lines, and proper termination.

Service Code Modernization

Layer / File(s) Summary
Pattern matching and collection expression refactoring
SysManager/SysManager/Services/AppAlertService.cs, SysManager/SysManager/Services/AppBlockerService.cs, SysManager/SysManager/Services/DeepCleanupService.cs, SysManager/SysManager/Services/DiskHealthService.cs, SysManager/SysManager/Services/EventLogService.cs, SysManager/SysManager/Services/HealthScoreService.cs, SysManager/SysManager/Services/IconExtractorService.cs, SysManager/SysManager/Services/MemoryTestService.cs, SysManager/SysManager/Services/PerformanceService.cs, SysManager/SysManager/Services/PingMonitorService.cs, SysManager/SysManager/Services/PowerShellRunner.cs, SysManager/SysManager/Services/ProcessDescriptionService.cs, SysManager/SysManager/Services/ShortcutCleanerService.cs, SysManager/SysManager/Services/SpeedTestService.cs, SysManager/SysManager/Services/StartupService.cs, SysManager/SysManager/Services/SystemInfoService.cs, SysManager/SysManager/Services/TracerouteService.cs, SysManager/SysManager/Services/TrayIconService.cs, SysManager/SysManager/Services/TuneUpService.cs, SysManager/SysManager/Services/UninstallerService.cs, SysManager/SysManager/Services/UpdateService.cs, SysManager/SysManager/Services/WindowsFeaturesService.cs, SysManager/SysManager/Services/WingetService.cs
Widespread refactoring applying C# pattern matching: null checks changed from == null/!= null to is null/is not null in registry access, resource validation, and conditional logic; list initializations changed from new List<T>() to collection expressions List<T> = [] for cleaner syntax. All functional behavior and control flow preserved.

Resource Management Enhancement

Layer / File(s) Summary
IDisposable implementation for services
SysManager/SysManager/Services/NetworkRepairService.cs, SysManager/SysManager/Services/SpeedTestHistoryService.cs
NetworkRepairService and SpeedTestHistoryService now implement IDisposable with Dispose() methods to clean up internal SemaphoreSlim resources; class declarations updated accordingly.

ViewModel Type Safety

Layer / File(s) Summary
ViewModel inheritance prevention
SysManager/SysManager/ViewModels/AboutViewModel.cs, SysManager/SysManager/ViewModels/AppAlertsViewModel.cs, SysManager/SysManager/ViewModels/AppBlockerViewModel.cs, SysManager/SysManager/ViewModels/AppUpdatesViewModel.cs, SysManager/SysManager/ViewModels/BatteryHealthViewModel.cs, SysManager/SysManager/ViewModels/CleanupViewModel.cs, SysManager/SysManager/ViewModels/ConsoleViewModel.cs, SysManager/SysManager/ViewModels/DashboardViewModel.cs, SysManager/SysManager/ViewModels/DeepCleanupViewModel.cs, SysManager/SysManager/ViewModels/DiskAnalyzerViewModel.cs, SysManager/SysManager/ViewModels/DriversViewModel.cs, SysManager/SysManager/ViewModels/DuplicateFileViewModel.cs, SysManager/SysManager/ViewModels/LogsViewModel.cs, SysManager/SysManager/ViewModels/MainWindowViewModel.cs, SysManager/SysManager/ViewModels/NetworkRepairViewModel.cs, SysManager/SysManager/ViewModels/PerformanceViewModel.cs, SysManager/SysManager/ViewModels/PingViewModel.cs, SysManager/SysManager/ViewModels/ProcessManagerViewModel.cs, SysManager/SysManager/ViewModels/ServicesViewModel.cs, SysManager/SysManager/ViewModels/ShortcutCleanerViewModel.cs, SysManager/SysManager/ViewModels/SpeedTestViewModel.cs, SysManager/SysManager/ViewModels/StartupViewModel.cs, SysManager/SysManager/ViewModels/SystemHealthViewModel.cs, SysManager/SysManager/ViewModels/TracerouteViewModel.cs, SysManager/SysManager/ViewModels/UninstallerViewModel.cs, SysManager/SysManager/ViewModels/WindowsFeaturesViewModel.cs, SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs
All 24 ViewModel classes changed from public partial to public sealed partial, preventing external subclassing while preserving partial-class composition support.

Minor Updates

Layer / File(s) Summary
License header and small edits
SysManager/SysManager/Views/AboutView.xaml.cs
Added MIT license comment header to code-behind file.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 A thousand null-checks transformed to patterns clean,
ViewModels sealed, and lists in brackets lean,
From 0.48 to 1.0, the version takes its bow,
.NET 10 embraces modernization now!
Tests stand ready, resources managed tight,
The codebase shines with C# delight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: a code quality pass with pattern modernization, documentation fixes, and test additions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/quality-pass

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
SysManager/SysManager/Services/SpeedTestHistoryService.cs (1)

17-38: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid disposing _fileLock while SaveAsync/ClearAsync still in-flight.

Both SaveAsync and ClearAsync follow the pattern: await _fileLock.WaitAsync()tryfinally { _fileLock.Release() }. If Dispose() is called while either method is between WaitAsync and the finally block, the Release() call will throw ObjectDisposedException. This is a real race condition because Dispose is not thread-safe and does not synchronize with in-flight operations.

Use a disposed flag with coordinated disposal to ensure no Release() calls execute after Dispose().

Proposed fix
 public sealed class SpeedTestHistoryService : IDisposable
 {
     private readonly SemaphoreSlim _fileLock = new(1, 1);
+    private int _disposed;

     /// <inheritdoc />
-    public void Dispose() => _fileLock.Dispose();
+    public void Dispose()
+    {
+        if (Interlocked.Exchange(ref _disposed, 1) == 1) return;
+        _fileLock.Wait();
+        _fileLock.Dispose();
+    }
+
+    private void ThrowIfDisposed()
+    {
+        if (Volatile.Read(ref _disposed) == 1)
+            throw new ObjectDisposedException(nameof(SpeedTestHistoryService));
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SysManager/SysManager/Services/SpeedTestHistoryService.cs` around lines 17 -
38, Add coordinated disposal: introduce a volatile bool _disposed, an atomic
counter (e.g. int _activeOps) and a TaskCompletionSource _noActiveOpsTcs; in
SaveAsync and ClearAsync increment _activeOps (Interlocked.Increment) before
calling _fileLock.WaitAsync(), check _disposed after increment and if set
decrement and throw ObjectDisposedException, and in the finally block decrement
_activeOps (Interlocked.Decrement) and if it reaches 0 and _disposed is true
call _noActiveOpsTcs.TrySetResult(true); in Dispose set _disposed = true and if
Interlocked.CompareExchange(ref _activeOps, _activeOps, _activeOps) > 0 await
_noActiveOpsTcs.Task before calling _fileLock.Dispose() (or await
_fileLock.WaitAsync/Release if you prefer) so no in-flight SaveAsync/ClearAsync
will later call _fileLock.Release() after disposal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@SysManager/SysManager/Services/NetworkRepairService.cs`:
- Around line 13-22: NetworkRepairService.Dispose currently disposes the
SemaphoreSlim _gate immediately, which can race with in-flight async operations
that call _gate.Release and cause ObjectDisposedException; fix by adding a
private volatile bool _disposed (or use int _disposedFlag), update public
Dispose() to set the disposed flag and then wait to enter the semaphore (e.g.
Acquire/Wait) before calling _gate.Dispose(), and ensure every async path that
uses _gate checks the disposed flag before waiting/releasing (throw
ObjectDisposedException or no-op release if disposed) so that Release() is never
called on a disposed semaphore; reference symbols: class NetworkRepairService,
field _gate, method Dispose().

---

Outside diff comments:
In `@SysManager/SysManager/Services/SpeedTestHistoryService.cs`:
- Around line 17-38: Add coordinated disposal: introduce a volatile bool
_disposed, an atomic counter (e.g. int _activeOps) and a TaskCompletionSource
_noActiveOpsTcs; in SaveAsync and ClearAsync increment _activeOps
(Interlocked.Increment) before calling _fileLock.WaitAsync(), check _disposed
after increment and if set decrement and throw ObjectDisposedException, and in
the finally block decrement _activeOps (Interlocked.Decrement) and if it reaches
0 and _disposed is true call _noActiveOpsTcs.TrySetResult(true); in Dispose set
_disposed = true and if Interlocked.CompareExchange(ref _activeOps, _activeOps,
_activeOps) > 0 await _noActiveOpsTcs.Task before calling _fileLock.Dispose()
(or await _fileLock.WaitAsync/Release if you prefer) so no in-flight
SaveAsync/ClearAsync will later call _fileLock.Release() after disposal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2708934a-5dc1-4bfc-9b40-be18c77574ee

📥 Commits

Reviewing files that changed from the base of the PR and between 43b1f2f and 625c708.

📒 Files selected for processing (62)
  • ARCHITECTURE.md
  • CONTRIBUTING.md
  • README.md
  • SECURITY.md
  • SysManager/SysManager.Tests/BulkObservableCollectionTests.cs
  • SysManager/SysManager.Tests/OperationLockServiceTests.cs
  • SysManager/SysManager.Tests/TestCollections.cs
  • SysManager/SysManager.Tests/WingetTableParserTests.cs
  • SysManager/SysManager/Services/AppAlertService.cs
  • SysManager/SysManager/Services/AppBlockerService.cs
  • SysManager/SysManager/Services/DeepCleanupService.cs
  • SysManager/SysManager/Services/DiskHealthService.cs
  • SysManager/SysManager/Services/EventLogService.cs
  • SysManager/SysManager/Services/HealthScoreService.cs
  • SysManager/SysManager/Services/IconExtractorService.cs
  • SysManager/SysManager/Services/MemoryTestService.cs
  • SysManager/SysManager/Services/NetworkRepairService.cs
  • SysManager/SysManager/Services/PerformanceService.cs
  • SysManager/SysManager/Services/PingMonitorService.cs
  • SysManager/SysManager/Services/PowerShellRunner.cs
  • SysManager/SysManager/Services/ProcessDescriptionService.cs
  • SysManager/SysManager/Services/ShortcutCleanerService.cs
  • SysManager/SysManager/Services/SpeedTestHistoryService.cs
  • SysManager/SysManager/Services/SpeedTestService.cs
  • SysManager/SysManager/Services/StartupService.cs
  • SysManager/SysManager/Services/SystemInfoService.cs
  • SysManager/SysManager/Services/TracerouteService.cs
  • SysManager/SysManager/Services/TrayIconService.cs
  • SysManager/SysManager/Services/TuneUpService.cs
  • SysManager/SysManager/Services/UninstallerService.cs
  • SysManager/SysManager/Services/UpdateService.cs
  • SysManager/SysManager/Services/WindowsFeaturesService.cs
  • SysManager/SysManager/Services/WingetService.cs
  • SysManager/SysManager/SysManager.csproj
  • SysManager/SysManager/ViewModels/AboutViewModel.cs
  • SysManager/SysManager/ViewModels/AppAlertsViewModel.cs
  • SysManager/SysManager/ViewModels/AppBlockerViewModel.cs
  • SysManager/SysManager/ViewModels/AppUpdatesViewModel.cs
  • SysManager/SysManager/ViewModels/BatteryHealthViewModel.cs
  • SysManager/SysManager/ViewModels/CleanupViewModel.cs
  • SysManager/SysManager/ViewModels/ConsoleViewModel.cs
  • SysManager/SysManager/ViewModels/DashboardViewModel.cs
  • SysManager/SysManager/ViewModels/DeepCleanupViewModel.cs
  • SysManager/SysManager/ViewModels/DiskAnalyzerViewModel.cs
  • SysManager/SysManager/ViewModels/DriversViewModel.cs
  • SysManager/SysManager/ViewModels/DuplicateFileViewModel.cs
  • SysManager/SysManager/ViewModels/LogsViewModel.cs
  • SysManager/SysManager/ViewModels/MainWindowViewModel.cs
  • SysManager/SysManager/ViewModels/NetworkRepairViewModel.cs
  • SysManager/SysManager/ViewModels/PerformanceViewModel.cs
  • SysManager/SysManager/ViewModels/PingViewModel.cs
  • SysManager/SysManager/ViewModels/ProcessManagerViewModel.cs
  • SysManager/SysManager/ViewModels/ServicesViewModel.cs
  • SysManager/SysManager/ViewModels/ShortcutCleanerViewModel.cs
  • SysManager/SysManager/ViewModels/SpeedTestViewModel.cs
  • SysManager/SysManager/ViewModels/StartupViewModel.cs
  • SysManager/SysManager/ViewModels/SystemHealthViewModel.cs
  • SysManager/SysManager/ViewModels/TracerouteViewModel.cs
  • SysManager/SysManager/ViewModels/UninstallerViewModel.cs
  • SysManager/SysManager/ViewModels/WindowsFeaturesViewModel.cs
  • SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs
  • SysManager/SysManager/Views/AboutView.xaml.cs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build & unit tests
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (61)
SysManager/SysManager/Views/AboutView.xaml.cs (1)

3-3: LGTM!

ARCHITECTURE.md (1)

3-3: LGTM!

CONTRIBUTING.md (1)

42-42: LGTM!

README.md (1)

18-18: LGTM!

SECURITY.md (1)

15-16: LGTM!

SysManager/SysManager/SysManager.csproj (1)

13-15: LGTM!

SysManager/SysManager/Services/AppAlertService.cs (1)

130-130: LGTM!

Also applies to: 137-137, 204-205, 212-212

SysManager/SysManager/Services/AppBlockerService.cs (1)

45-45: LGTM!

Also applies to: 90-90, 93-93, 96-96, 140-140, 143-143, 146-146, 163-163, 170-170, 173-173

SysManager/SysManager/Services/DeepCleanupService.cs (1)

269-269: LGTM!

Also applies to: 283-283, 319-319, 404-404

SysManager/SysManager/Services/DiskHealthService.cs (1)

149-149: LGTM!

Also applies to: 163-163, 172-172, 181-181

SysManager/SysManager/Services/EventLogService.cs (1)

51-51: LGTM!

Also applies to: 59-59, 136-136

SysManager/SysManager/Services/HealthScoreService.cs (1)

103-103: LGTM!

Also applies to: 118-118, 137-137, 153-153, 177-177, 180-180, 191-191, 203-203, 213-213

SysManager/SysManager/Services/IconExtractorService.cs (1)

79-79: LGTM!

Also applies to: 87-87, 120-120, 135-135, 274-274, 295-295, 376-376, 393-393

SysManager/SysManager/Services/MemoryTestService.cs (1)

61-62: LGTM!

SysManager/SysManager/Services/PerformanceService.cs (1)

93-93: LGTM!

Also applies to: 147-148, 169-169, 216-216, 240-240, 295-295, 327-327, 329-329, 341-341, 343-343, 379-379, 386-386, 425-425, 445-445, 470-470, 616-616

SysManager/SysManager/Services/PingMonitorService.cs (1)

133-133: LGTM!

SysManager/SysManager/Services/PowerShellRunner.cs (1)

76-76: LGTM!

SysManager/SysManager/Services/ProcessDescriptionService.cs (1)

108-108: LGTM!

Also applies to: 115-115, 126-126

SysManager/SysManager/Services/ShortcutCleanerService.cs (1)

106-106: LGTM!

SysManager/SysManager/Services/SpeedTestService.cs (1)

60-60: LGTM!

Also applies to: 391-391

SysManager/SysManager/Services/StartupService.cs (1)

99-99: LGTM!

Also applies to: 120-121, 155-155, 162-162, 165-165, 225-225, 280-280, 296-296, 331-331, 416-416

SysManager/SysManager/Services/SystemInfoService.cs (1)

151-151: LGTM!

Also applies to: 174-174

SysManager/SysManager/Services/TracerouteService.cs (1)

35-35: LGTM!

Also applies to: 72-72, 102-102

SysManager/SysManager/Services/TrayIconService.cs (1)

155-155: LGTM!

Also applies to: 205-205

SysManager/SysManager/Services/TuneUpService.cs (1)

84-84: LGTM!

SysManager/SysManager/Services/UninstallerService.cs (1)

31-31: LGTM!

Also applies to: 128-128, 141-142, 157-157, 189-189

SysManager/SysManager/Services/UpdateService.cs (1)

72-72: LGTM!

Also applies to: 104-104, 310-310, 331-331, 334-334

SysManager/SysManager/Services/WindowsFeaturesService.cs (1)

27-27: LGTM!

Also applies to: 58-58, 92-92

SysManager/SysManager/Services/WingetService.cs (1)

30-30: LGTM!

SysManager/SysManager/ViewModels/AboutViewModel.cs (1)

20-20: LGTM!

SysManager/SysManager/ViewModels/AppAlertsViewModel.cs (1)

20-20: LGTM!

SysManager/SysManager/ViewModels/AppBlockerViewModel.cs (1)

20-20: LGTM!

SysManager/SysManager/ViewModels/AppUpdatesViewModel.cs (1)

15-15: LGTM!

SysManager/SysManager/ViewModels/BatteryHealthViewModel.cs (1)

17-17: LGTM!

SysManager/SysManager/ViewModels/CleanupViewModel.cs (1)

15-15: LGTM!

SysManager/SysManager/ViewModels/ConsoleViewModel.cs (1)

17-17: LGTM!

SysManager/SysManager/ViewModels/DashboardViewModel.cs (1)

15-15: LGTM!

SysManager/SysManager/ViewModels/DeepCleanupViewModel.cs (1)

17-17: LGTM!

SysManager/SysManager/ViewModels/DiskAnalyzerViewModel.cs (1)

20-20: LGTM!

SysManager/SysManager/ViewModels/DriversViewModel.cs (1)

17-17: LGTM!

SysManager/SysManager/ViewModels/DuplicateFileViewModel.cs (1)

21-21: LGTM!

SysManager/SysManager/ViewModels/LogsViewModel.cs (1)

26-26: LGTM!

SysManager/SysManager/ViewModels/MainWindowViewModel.cs (1)

15-15: LGTM!

SysManager/SysManager/ViewModels/NetworkRepairViewModel.cs (1)

15-15: LGTM!

SysManager/SysManager/ViewModels/PerformanceViewModel.cs (1)

25-25: LGTM!

SysManager/SysManager/ViewModels/PingViewModel.cs (1)

16-16: LGTM!

SysManager/SysManager/ViewModels/ProcessManagerViewModel.cs (1)

19-19: LGTM!

SysManager/SysManager/ViewModels/ServicesViewModel.cs (1)

21-21: LGTM!

SysManager/SysManager/ViewModels/ShortcutCleanerViewModel.cs (1)

18-18: LGTM!

SysManager/SysManager/ViewModels/SpeedTestViewModel.cs (1)

16-16: LGTM!

SysManager/SysManager/ViewModels/StartupViewModel.cs (1)

19-19: LGTM!

SysManager/SysManager/ViewModels/SystemHealthViewModel.cs (1)

18-18: LGTM!

SysManager/SysManager/ViewModels/TracerouteViewModel.cs (1)

17-17: LGTM!

SysManager/SysManager/ViewModels/UninstallerViewModel.cs (1)

17-17: LGTM!

SysManager/SysManager/ViewModels/WindowsFeaturesViewModel.cs (1)

19-19: LGTM!

SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs (1)

17-17: LGTM!

SysManager/SysManager/Services/SpeedTestHistoryService.cs (1)

55-55: LGTM!

Also applies to: 134-134

SysManager/SysManager.Tests/OperationLockServiceTests.cs (1)

10-10: LGTM!

SysManager/SysManager.Tests/TestCollections.cs (1)

14-19: LGTM!

SysManager/SysManager.Tests/WingetTableParserTests.cs (1)

1-113: LGTM!

SysManager/SysManager.Tests/BulkObservableCollectionTests.cs (1)

1-90: LGTM!

Comment on lines +13 to +22
public sealed class NetworkRepairService : IDisposable
{
private readonly PowerShellRunner _ps;
private readonly SemaphoreSlim _gate = new(1, 1);

public NetworkRepairService(PowerShellRunner ps) => _ps = ps;

/// <inheritdoc />
public void Dispose() => _gate.Dispose();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

❓ Verification inconclusive

In .NET, is SemaphoreSlim.Dispose safe when concurrent WaitAsync/Release may still occur, and what disposal pattern is recommended to avoid ObjectDisposedException?


No, calling SemaphoreSlim.Dispose while concurrent WaitAsync or Release operations are ongoing is not thread-safe [1][2][3]. Official Microsoft documentation explicitly states that all public and protected members of SemaphoreSlim are thread-safe, with the sole exception of Dispose, which must be used only when all other operations on the instance have completed [1][2]. Attempting to dispose of the semaphore while other threads are waiting or attempting to release can lead to race conditions, including tasks hanging indefinitely or throwing unexpected ObjectDisposedException errors [4]. Recommended Disposal Pattern To safely dispose of a SemaphoreSlim and avoid ObjectDisposedException, you must ensure that no further operations will attempt to use the instance before calling Dispose. The following strategies are recommended: 1. Use Cooperative Cancellation: Before disposing, signal any waiting tasks to cancel their operations using a CancellationToken [5][6]. Ensure your code handles OperationCanceledException and stops calling WaitAsync or Release on that semaphore instance [5]. 2. Track Pending Operations: Maintain a mechanism to track active tasks or operations that use the semaphore. Before calling Dispose, you must guarantee these operations have finished [5]. For example, you can use a list of tasks or a reference counter to track active work and await their completion: // Example logic for safe disposal public void Dispose { // 1. Signal cancellation if applicable _cancellationTokenSource.Cancel; // 2. Wait for all tracked tasks to complete before disposing Task.WaitAll(_activeTasks.ToArray); // 3. Finally, dispose the semaphore _semaphore.Dispose; } 3. Avoid Concurrent Access During Teardown: If your object owns the SemaphoreSlim, ensure the disposing object's lifecycle logic prevents new calls to WaitAsync. Do not rely on "dirty workarounds" like skipping Dispose, as failing to dispose objects that use unmanaged resources (such as when accessing AvailableWaitHandle) can lead to resource leaks [7][8]. Always treat SemaphoreSlim as a standard IDisposable object and manage its lifetime within your class's own Dispose pattern [7][8].

Citations:


Script executed:

cat -n SysManager/SysManager/Services/NetworkRepairService.cs

Repository: laurentiu021/SystemManager


Repository: laurentiu021/SystemManager
Exit code: 0

stdout:

     1	// SysManager · NetworkRepairService
     2	// Author: laurentiu021 · https://github.com/laurentiu021/SystemManager
     3	// License: MIT
     4	
     5	using SysManager.Models;
     6	
     7	namespace SysManager.Services;
     8	
     9	/// <summary>
    10	/// Runs common network repair commands: DNS flush, Winsock reset, TCP/IP reset.
    11	/// Each method captures stdout/stderr and returns a <see cref="NetworkRepairResult"/>.
    12	/// </summary>
    13	public sealed class NetworkRepairService : IDisposable
    14	{
    15	    private readonly PowerShellRunner _ps;
    16	    private readonly SemaphoreSlim _gate = new(1, 1);
    17	
    18	    public NetworkRepairService(PowerShellRunner ps) => _ps = ps;
    19	
    20	    /// <inheritdoc />
    21	    public void Dispose() => _gate.Dispose();
    22	
    23	    /// <summary>
    24	    /// Flush the DNS resolver cache. Does not require a reboot.
    25	    /// </summary>
    26	    public async Task<NetworkRepairResult> FlushDnsAsync(CancellationToken ct = default)
    27	    {
    28	        await _gate.WaitAsync(ct).ConfigureAwait(false);
    29	        var output = new System.Collections.Concurrent.ConcurrentQueue<string>();
    30	        void OnLine(PowerShellLine line) => output.Enqueue(line.Text);
    31	        _ps.LineReceived += OnLine;
    32	        try
    33	        {
    34	            var exit = await _ps.RunProcessAsync("ipconfig.exe", "/flushdns", ct, PowerShellRunner.OemEncoding)
    35	                .ConfigureAwait(false);
    36	            return new NetworkRepairResult(
    37	                "DNS Flush",
    38	                exit == 0,
    39	                string.Join(Environment.NewLine, output),
    40	                NeedsReboot: false);
    41	        }
    42	        finally { _ps.LineReceived -= OnLine; _gate.Release(); }
    43	    }
    44	
    45	    /// <summary>
    46	    /// Reset the Winsock catalog. Requires a reboot to take effect.
    47	    /// </summary>
    48	    public async Task<NetworkRepairResult> ResetWinsockAsync(CancellationToken ct = default)
    49	    {
    50	        await _gate.WaitAsync(ct).ConfigureAwait(false);
    51	        var output = new System.Collections.Concurrent.ConcurrentQueue<string>();
    52	        void OnLine(PowerShellLine line) => output.Enqueue(line.Text);
    53	        _ps.LineReceived += OnLine;
    54	        try
    55	        {
    56	            var exit = await _ps.RunProcessAsync("netsh.exe", "winsock reset", ct, PowerShellRunner.OemEncoding)
    57	                .ConfigureAwait(false);
    58	            return new NetworkRepairResult(
    59	                "Winsock Reset",
    60	                exit == 0,
    61	                string.Join(Environment.NewLine, output),
    62	                NeedsReboot: true);
    63	        }
    64	        finally { _ps.LineReceived -= OnLine; _gate.Release(); }
    65	    }
    66	
    67	    /// <summary>
    68	    /// Reset the TCP/IP stack. Requires a reboot to take effect.
    69	    /// </summary>
    70	    public async Task<NetworkRepairResult> ResetTcpIpAsync(CancellationToken ct = default)
    71	    {
    72	        await _gate.WaitAsync(ct).ConfigureAwait(false);
    73	        var output = new System.Collections.Concurrent.ConcurrentQueue<string>();
    74	        void OnLine(PowerShellLine line) => output.Enqueue(line.Text);
    75	        _ps.LineReceived += OnLine;
    76	        try
    77	        {
    78	            var exit = await _ps.RunProcessAsync("netsh.exe", "int ip reset", ct, PowerShellRunner.OemEncoding)
    79	                .ConfigureAwait(false);
    80	            return new NetworkRepairResult(
    81	                "TCP/IP Reset",
    82	                exit == 0,
    83	                string.Join(Environment.NewLine, output),
    84	                NeedsReboot: true);
    85	        }
    86	        finally { _ps.LineReceived -= OnLine; _gate.Release(); }
    87	    }
    88	}


Synchronize semaphore disposal with in-flight async operations.

Dispose() immediately disposes _gate without coordination; if an async method is in-flight, its finally block will call _gate.Release() on a disposed semaphore, causing ObjectDisposedException. This is unsafe per .NET threading guarantees.

Implement disposal synchronization using a disposed flag and acquire the semaphore before disposal:

Proposed fix
public sealed class NetworkRepairService : IDisposable
{
    private readonly PowerShellRunner _ps;
    private readonly SemaphoreSlim _gate = new(1, 1);
+   private int _disposed;

    public NetworkRepairService(PowerShellRunner ps) => _ps = ps;

    /// <inheritdoc />
-   public void Dispose() => _gate.Dispose();
+   public void Dispose()
+   {
+       if (Interlocked.Exchange(ref _disposed, 1) == 1) return;
+       _gate.Wait();
+       _gate.Dispose();
+   }
+
+   private void ThrowIfDisposed()
+   {
+       if (Volatile.Read(ref _disposed) == 1)
+           throw new ObjectDisposedException(nameof(NetworkRepairService));
+   }
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SysManager/SysManager/Services/NetworkRepairService.cs` around lines 13 - 22,
NetworkRepairService.Dispose currently disposes the SemaphoreSlim _gate
immediately, which can race with in-flight async operations that call
_gate.Release and cause ObjectDisposedException; fix by adding a private
volatile bool _disposed (or use int _disposedFlag), update public Dispose() to
set the disposed flag and then wait to enter the semaphore (e.g. Acquire/Wait)
before calling _gate.Dispose(), and ensure every async path that uses _gate
checks the disposed flag before waiting/releasing (throw ObjectDisposedException
or no-op release if disposed) so that Release() is never called on a disposed
semaphore; reference symbols: class NetworkRepairService, field _gate, method
Dispose().

@laurentiu021
laurentiu021 merged commit 3b8867a into main May 20, 2026
5 checks passed
@laurentiu021
laurentiu021 deleted the chore/quality-pass branch May 20, 2026 15:10
laurentiu021 added a commit that referenced this pull request May 22, 2026
…bs (#468)

## Summary

Reorganizes the sidebar navigation from 7 groups / 21 tabs to **9 groups
/ 36 tabs**, adding WIP placeholder tabs for all planned features so the
structure is final and future PRs only need to replace the placeholder
with the real implementation.

## Changes

### New groups
- **Monitor** (4 tabs): Process Manager (moved from System), Resource
History, App Alerts, Privacy Monitor
- **Control** (5 tabs): Privacy Settings, Context Menu, Restore Points,
Scheduled Maintenance, System Report

### Expanded groups
- **System** +1: Windows Features
- **Cleanup** +2: Shortcut Cleaner, File Shredder
- **Network** +2: DNS Changer, Hosts Editor
- **Apps** +2: Bulk Installer, App Blocker

### New files
- \PlaceholderViewModel.cs\ — lightweight VM storing feature name,
description, issue number
- \PlaceholderView.xaml\ + code-behind — WIP page with feature name,
badge, description, issue ref
- \PlaceholderViewModelTests.cs\ — 4 unit tests

### Updated files
- \MainWindowViewModel.cs\ — 9 groups, 36 tabs, 15 WIP placeholder
instances
- \MainWindowViewModelTests.cs\ (IntegrationTests) — updated assertions
for new counts
- \CHANGELOG.md\ — v0.29.0 entry

## Testing
- Build: 0 errors (main project + tests)
- All existing tests unaffected (PlaceholderViewModel is additive)
- 4 new unit tests for PlaceholderViewModel

Closes #393

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
laurentiu021 added a commit that referenced this pull request May 22, 2026
Updates documentation to reflect the new sidebar structure from PR #468
(v0.29.0).

- README: sidebar table updated from 7 groups / 21 tabs to 9 groups / 36
tabs, with ⚙️ markers for WIP tabs
- ARCHITECTURE: tabs table updated with new groups (Monitor, Control)
and PlaceholderViewModel references

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
laurentiu021 added a commit that referenced this pull request May 22, 2026
…UAL-007) (#468)

- Seal all 27 ViewModels (none are subclassed)
- Modernize null checks to pattern matching (is null / is not null) in 16 services
- Replace new List<T>() with collection expressions in 14 services
- Fix IDisposable: NetworkRepairService, SpeedTestHistoryService now dispose SemaphoreSlim
- Fix test isolation: add CollectionDefinition for OperationLock singleton tests
- Add BulkObservableCollectionTests (6 tests) and WingetTableParserTests (6 tests)
- Fix docs: README badge, CONTRIBUTING, ARCHITECTURE, SECURITY versions → .NET 10 / v1.0.0
- Fix SysManager.csproj Version/FileVersion/AssemblyVersion → 1.0.0
- Fix AboutView.xaml.cs missing License header line

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
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.

1 participant