Skip to content

fix: FanCurve thread safety with proper locking - #22

Merged
beenycool merged 3 commits into
masterfrom
fix/fancurve-threadsafety
Mar 20, 2026
Merged

fix: FanCurve thread safety with proper locking#22
beenycool merged 3 commits into
masterfrom
fix/fancurve-threadsafety

Conversation

@beenycool

@beenycool beenycool commented Mar 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Make Points property private with thread-safe getter returning a copy
  • Add thread-safe APIs: SetPoints, AddPoint, RemovePointAt, UpdatePointAt, PointCount, ClearPoints
  • All internal operations now lock properly to prevent concurrent modification

Summary by CodeRabbit

  • Bug Fixes
    • Improved application stability during startup and shutdown with better synchronization handling.
    • Enhanced robustness of fan operations with stronger validation and error handling.
    • Fixed fan curve point ordering to ensure curves are consistently sorted by temperature, regardless of input order.

@coderabbitai

coderabbitai Bot commented Mar 17, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: cc5b9457-d8e2-4fa2-a30f-05cb2b03b39e

📥 Commits

Reviewing files that changed from the base of the PR and between 460792c and 92f5c01.

📒 Files selected for processing (4)
  • AsusFanControl.Core/AsusControl.cs
  • AsusFanControl.Core/FanCurve.cs
  • AsusFanControlGUI/FanCurveControl.cs
  • AsusFanControlGUI/Form1.cs

Walkthrough

This PR enhances thread-safety and lifecycle management in AsusControl by adding WinIO shutdown synchronization via static flags and Monitor coordination, instance tracking, and safe disposal patterns. It also adds validation and cloning semantics to FanCurve points and updates curve initialization to sort points by temperature.

Changes

Cohort / File(s) Summary
Core lifecycle & thread-safety
AsusFanControl.Core/AsusControl.cs
Adds static _shutdownInProgress flag and Monitor.Wait/PulseAll coordination in constructor; wraps first-instance initialization in try/catch with fallback ShutdownWinIo(). Enhances Dispose(bool) with instance counting, last-instance cleanup/reset sequencing, and _disposing checks across hardware operations (UpdateFanSpeeds, MonitorLoop, SetFanSpeed, GetFanSpeed, GetFanSpeeds). Introduces IsValidFanIndex() validation and refactors SetFanSpeeds(int) to fire-and-forget a private async variant. Hardens ResetToDefaultInternal() with per-fan try/catch and direct HealthyTable commands replacing indirect SetFanSpeed() calls.
FanCurve validation & cloning
AsusFanControl.Core/FanCurve.cs
Changes _points to non-readonly and refactors all point access to lock, clone, and validate. GetTargetSpeed now locks and clones _points locally; SetPoints validates and clones each incoming point; AddPoint and UpdatePointAt validate and clone before storing. Adds helper methods ValidatePoint (enforces 0–100 ranges) and ClonePoint (creates new instance). Points property now returns cloned snapshot via Select(ClonePoint).ToList().
GUI curve initialization
AsusFanControlGUI/FanCurveControl.cs, AsusFanControlGUI/Form1.cs
FanCurveControl.GetCurve() now uses SetPoints() with sorted points (OrderBy(Temperature)) instead of iterating and calling AddPoint(). Form1 constructor updates default curve fallback to use SetPoints() in one operation instead of incremental AddPoint() calls.

Sequence Diagram

sequenceDiagram
    participant C1 as Constructor 1
    participant C2 as Constructor 2
    participant Monitor as Hardware Lock<br/>(Monitor)
    participant Dispose as Dispose (Last<br/>Instance)
    participant WinIO as WinIO Shutdown

    C1->>Monitor: Acquire _hardwareLock
    C1->>Monitor: Check _shutdownInProgress<br/>(false, proceed)
    C1->>WinIO: Initialize WinIO
    C1->>Monitor: Release _hardwareLock
    
    C2->>Monitor: Acquire _hardwareLock
    C2->>Monitor: Check _shutdownInProgress<br/>(false, proceed)
    C2->>WinIO: Initialize WinIO
    C2->>Monitor: Release _hardwareLock

    Dispose->>Monitor: Acquire _hardwareLock
    Dispose->>Monitor: Decrement _instanceCount<br/>(reaches 0)
    Dispose->>Monitor: Set _shutdownInProgress = true
    Dispose->>Monitor: Release _hardwareLock
    
    C1->>Monitor: (New) Acquire _hardwareLock
    C1->>Monitor: Check _shutdownInProgress<br/>(true)
    C1->>Monitor: Monitor.Wait() - BLOCK
    
    Dispose->>WinIO: ResetToDefaultInternal()
    Dispose->>WinIO: ShutdownWinIo()
    Dispose->>Monitor: Acquire _hardwareLock
    Dispose->>Monitor: Set _shutdownInProgress = false
    Dispose->>Monitor: Monitor.PulseAll()
    Dispose->>Monitor: Release _hardwareLock
    
    C1->>Monitor: Unblocked, Acquire _hardwareLock
    C1->>WinIO: Initialize WinIO (new instance)
    C1->>Monitor: Release _hardwareLock
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 Whiskers aquiver with delight
Locks and waits keep threads just right,
Points now cloned and validated true,
Shutdown signals clear and new!
Safe disposal, clean and bright! 🌟

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title focuses on FanCurve thread safety, but the changeset includes substantial parallel changes to AsusControl (instance counting, hardware locking, async reset) that are equally or more significant. Consider a more comprehensive title such as 'fix: Thread safety improvements across FanCurve and AsusControl with locking and instance coordination' to accurately reflect the full scope of changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 fix/fancurve-threadsafety
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the thread safety of the AsusFanControl.Core library by implementing robust locking mechanisms for both fan curve management and direct hardware interactions. The changes ensure that concurrent access to fan settings and hardware control methods is properly synchronized, preventing data corruption and unexpected behavior in multi-threaded environments. This improves the stability and reliability of the fan control functionality.

Highlights

  • FanCurve Thread Safety: The FanCurve class's Points property has been made private, with a thread-safe getter returning a copy. New thread-safe APIs (SetPoints, AddPoint, RemovePointAt, UpdatePointAt, PointCount, ClearPoints) were added to manage fan curve points, ensuring all internal operations are properly locked to prevent concurrent modification issues.
  • AsusControl Hardware Access Thread Safety: The AsusControl class now includes thread-safe initialization and shutdown of the underlying WinIO library. All methods interacting with hardware, such as SetFanSpeed, GetFanSpeed, Thermal_Read_Cpu_Temperature, and ResetToDefault, have been wrapped with a static lock (_hwLock) to prevent race conditions during concurrent access.
  • AsusControl Resource Management Improvements: The AsusControl class now tracks instance counts to ensure AsusWinIO64 is initialized and shut down only once across multiple instances. The number of fans (_fanCount) is cached on initialization to avoid repeated WinIO calls, and error handling has been added to the dispose process for robustness.
  • Asynchronous Reset Method: An asynchronous ResetToDefaultAsync method has been added to the IFanController interface and implemented in AsusControl, providing a non-blocking way to reset fan settings.
Changelog
  • AsusFanControl.Core/AsusControl.cs
    • Added System.Diagnostics import.
    • Implemented thread-safe initialization and shutdown of AsusWinIO64 using a static lock and instance counter.
    • Added a _fanCount field to cache the number of fans.
    • Introduced a _hwLock object for synchronizing hardware access.
    • Wrapped SetFanSpeed, GetFanSpeed, Thermal_Read_Cpu_Temperature, and ResetToDefault methods with _hwLock.
    • Refactored ResetToDefault into ResetToDefaultInternal and added a public ResetToDefaultAsync method.
    • Removed the async Task SetFanSpeeds(byte value) private method, integrating its logic directly into SetFanSpeeds(int percent).
    • Added error handling (try-catch with Debug.WriteLine) for WinIO shutdown and fan reset during dispose.
  • AsusFanControl.Core/FanCurve.cs
    • Changed Points property from public List<FanCurvePoint> to private _points with a thread-safe IReadOnlyList<FanCurvePoint> getter.
    • Introduced a _lock object for synchronizing access to _points.
    • Implemented new public methods: SetPoints, AddPoint, RemovePointAt, UpdatePointAt, PointCount, and ClearPoints, all using _lock.
    • Modified GetTargetSpeed to operate on a thread-safe snapshot of points and added a check for pre-sorted points.
    • Updated ToString() to use a thread-safe snapshot of points.
    • Modified FromString to use SetPoints for populating the curve.
  • AsusFanControl.Core/IFanController.cs
    • Added System.Threading.Tasks import.
    • Added ResetToDefaultAsync() to the interface.
Activity
  • The author beenyccol initiated this pull request to address thread safety concerns.
  • The pull request description clearly outlines the intent and specific changes related to thread-safe FanCurve operations and AsusControl hardware access.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request significantly improves thread safety for hardware access and fan curve management by introducing proper locking. The changes in AsusControl.cs correctly use a lock for hardware calls and manage the lifecycle of the WinIo library with an instance counter. Similarly, FanCurve.cs is refactored to be thread-safe for all its operations. My review includes a critical fix for a performance anti-pattern where a lock is held during a Thread.Sleep, and a minor code cleanup suggestion.

Comment on lines +179 to 196
private void ResetToDefaultInternal()
{
var fanCount = _fanCount;
for (byte fanIndex = 0; fanIndex < fanCount; fanIndex++)
{
SetFanSpeed(0, fanIndex);
// Minimal blocking delay to ensure hardware processes the command if needed,
// but keep it fast for shutdown.
try
{
AsusWinIO64.HealthyTable_SetFanIndex(fanIndex);
AsusWinIO64.HealthyTable_SetFanTestMode(FanModeDefault);
AsusWinIO64.HealthyTable_SetFanPwmDuty(0);
}
catch (Exception ex)
{
Debug.WriteLine($"[AsusControl] Failed to reset fan {fanIndex}: {ex.Message}");
}
System.Threading.Thread.Sleep(ResetCommandDelayMs);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This method is called from within a lock by ResetToDefault and Dispose, but it contains a Thread.Sleep. Holding a lock while sleeping is an anti-pattern that can cause performance issues and thread contention, as it blocks other threads from accessing the hardware for an unnecessarily long time. To fix this, this method should perform its own locking inside the loop for the brief hardware access, and the callers should be updated to not lock around the call to this method. This suggestion refactors this method to perform its own locking; please ensure the callers (ResetToDefault and Dispose) are updated accordingly to not lock around this method call.

private void ResetToDefaultInternal()
{
    var fanCount = _fanCount;
    for (byte fanIndex = 0; fanIndex < fanCount; fanIndex++)
    {
        try
        {
            lock (_hwLock)
            {
                if (_disposed) return;
                AsusWinIO64.HealthyTable_SetFanIndex(fanIndex);
                AsusWinIO64.HealthyTable_SetFanTestMode(FanModeDefault);
                AsusWinIO64.HealthyTable_SetFanPwmDuty(0);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[AsusControl] Failed to reset fan {fanIndex}: {ex.Message}");
        }
        System.Threading.Thread.Sleep(ResetCommandDelayMs);
    }
}

Comment thread AsusFanControl.Core/FanCurve.cs Outdated
List<FanCurvePoint> sortedPoints;
lock (_lock)
{
if (_points == null || _points.Count == 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The _points field is initialized with a new list and is never reassigned or set to null. Therefore, the _points == null check is redundant and can be removed for improved code clarity.

                if (_points.Count == 0)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 460792ce0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread AsusFanControl.Core/AsusControl.cs Outdated
Comment on lines +54 to +57
if (disposing)
{
_instanceCount--;
if (_instanceCount == 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Decrement instance count in finalizer disposal path

The new Dispose(bool disposing) logic only decrements _instanceCount when disposing is true, so objects finalized without an explicit Dispose() leave the static count permanently elevated. After that happens once, later disposals may never hit _instanceCount == 0, which skips both ResetToDefaultInternal() and ShutdownWinIo(), leaving fan state and driver lifetime management inconsistent for the rest of the process.

Useful? React with 👍 / 👎.

Comment thread AsusFanControl.Core/AsusControl.cs Outdated
public Task ResetToDefaultAsync()
{
if (_disposed) return Task.CompletedTask;
ResetToDefaultInternal();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Serialize ResetToDefaultAsync with hardware lock

ResetToDefaultAsync() calls ResetToDefaultInternal() without taking _hwLock, unlike other hardware-touching methods. If this async API is called concurrently with SetFanSpeed, GetFanSpeed, or Dispose, P/Invoke calls can interleave with ShutdownWinIo() and reintroduce the same hardware race conditions this change is trying to eliminate.

Useful? React with 👍 / 👎.

@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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AsusFanControl.Core/AsusControl.cs`:
- Around line 83-91: Acquire the hardware lock before checking disposal in
SetFanSpeed: move the if (_disposed) return; inside the lock block so you
lock(_hwLock) { if (_disposed) return; ... } and then call
AsusWinIO64.HealthyTable_*; apply the same change to the other fan-control
methods that take _hwLock and call AsusWinIO64 P/Invoke (the similar methods
that perform fan PWM/tacho/test-mode updates) so disposal is always checked
while holding _hwLock.
- Around line 162-166: ResetToDefaultAsync currently calls
ResetToDefaultInternal() directly, bypassing the synchronization and disposal
checks used elsewhere; change ResetToDefaultAsync to reuse the public
ResetToDefault() method (which performs the locking/disposal validation) instead
of calling ResetToDefaultInternal(), so ResetToDefaultAsync invokes
ResetToDefault() and then returns a completed Task, preserving thread-safety
with SetFanSpeed, GetFanSpeed, and Dispose checks.
- Around line 23-31: The constructor increments _instanceCount while holding
_hwLock but calls AsusWinIO64.HealthyTable_FanCounts() outside the lock, which
can leave _instanceCount inconsistent if the probe fails; fix by performing the
fan-count probe and any initialization inside the same _hwLock critical section
(or, alternatively, if you prefer to keep the increment before probing, catch
any exception from HealthyTable_FanCounts(), decrement _instanceCount and
rethrow), so update the code around _hwLock/_instanceCount to call
AsusWinIO64.InitializeWinIo() and AsusWinIO64.HealthyTable_FanCounts() inside
the lock (or ensure a rollback decrement of _instanceCount on probe failure) and
assign _fanCount only after a successful probe.

In `@AsusFanControl.Core/FanCurve.cs`:
- Around line 97-109: SetPoints currently clears and mutates _points while
enumerating newPoints, which can leave the curve partially replaced if
enumeration throws or contains nulls; instead, materialize newPoints into a
temporary list (e.g., var temp = new List<FanCurvePoint>()), validate each
element (ensure not null and its Temperature/Speed are valid) while cloning
values with new FanCurvePoint(p.Temperature, p.Speed), and only after successful
materialization and validation enter the lock and replace _points atomically
(clear and add range or swap contents) under _lock to avoid partial updates.
- Around line 26-33: The Points getter currently returns a shallow copy of the
_points list allowing callers to mutate the same FanCurvePoint instances; modify
the Points property to produce and return deep copies while holding the lock
(e.g., lock(_lock) { return _points.Select(p => new FanCurvePoint(p.Temperature,
p.FanSpeed /*or use a Clone() method*/)).ToList(); }), or alternatively make
FanCurvePoint immutable and construct new instances when mutating the curve;
ensure all copies are created inside the lock to prevent race conditions and
reference the _points, _lock, Points and FanCurvePoint identifiers when applying
the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b0d371c4-2426-41e6-9499-d01b93a2fb0a

📥 Commits

Reviewing files that changed from the base of the PR and between 746b222 and 460792c.

📒 Files selected for processing (3)
  • AsusFanControl.Core/AsusControl.cs
  • AsusFanControl.Core/FanCurve.cs
  • AsusFanControl.Core/IFanController.cs

Comment thread AsusFanControl.Core/AsusControl.cs Outdated
Comment thread AsusFanControl.Core/AsusControl.cs
Comment thread AsusFanControl.Core/AsusControl.cs
Comment thread AsusFanControl.Core/FanCurve.cs
Comment thread AsusFanControl.Core/FanCurve.cs
beenycool added a commit that referenced this pull request Mar 20, 2026
@beenycool
beenycool force-pushed the fix/fancurve-threadsafety branch from 460792c to 5f2f419 Compare March 20, 2026 12:51
@kilo-code-bot

kilo-code-bot Bot commented Mar 20, 2026

Copy link
Copy Markdown

Code Review Summary

Status: Issues Already Documented | Recommendation: Address existing comments before merge

Overview

The PR implements thread-safety improvements for FanCurve and AsusControl. The existing review comments already identify the critical issues in this PR.

Severity Count
CRITICAL 1
WARNING 0
SUGGESTION 0
Existing Critical Issues (already documented)
File Line Issue
AsusFanControl.Core/AsusControl.cs ~314 Lock held during Thread.Sleep - causes thread contention
AsusFanControl.Core/AsusControl.cs ~23-31 Instance count can become inconsistent on constructor failure
AsusFanControl.Core/AsusControl.cs ~283-287 ResetToDefaultAsync doesn't serialize with hardware lock
AsusFanControl.Core/FanCurve.cs ~32 Points getter returns shallow copies (already fixed with cloning)
Verification

Verified the following fixes in the current diff:

  • ✅ Constructor now wraps fan count probe in try/catch with proper cleanup
  • ResetToDefaultInternal now acquires lock per-iteration (though still holds during hardware calls)
  • ✅ FanCurve.Points returns deep copies via ClonePoint
  • ✅ All hardware methods check _disposed || _disposing inside lock
Files Reviewed (6 files)
  • AsusFanControl.Core/AsusControl.cs - thread-safety improvements
  • AsusFanControl.Core/FanCurve.cs - thread-safe collection
  • AsusFanControl.Core/IFanController.cs - interface update
  • AsusFanControlGUI/FanCurveControl.cs - uses new thread-safe API
  • AsusFanControlGUI/FanCurveEditor.cs - uses SetPoints
  • AsusFanControlGUI/Form1.cs - uses new API

}

_fanCount = AsusWinIO64.HealthyTable_FanCounts();
_instanceCount++;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CRITICAL: Instance count leak on exception

If AsusWinIO64.HealthyTable_FanCounts() throws after _instanceCount++ has already executed, the catch block does not decrement _instanceCount. This leaves the count elevated, causing subsequent instances to skip initialization (since _instanceCount > 0) while WinIO is actually uninitialized.

@@ -156,75 +230,89 @@ public void SetFanSpeed(int percent, byte fanIndex = 0)
private async Task SetFanSpeeds(byte value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Missing bounds check on fanIndex

No validation that fanIndex is within valid range (0 to _fanCount - 1). An out-of-range index could cause undefined behavior with the hardware P/Invoke calls.

public ulong Thermal_Read_Cpu_Temperature()
{
if (_disposed) return 0;
lock (_hardwareLock)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Missing bounds check on fanIndex

No validation that fanIndex is within valid range (0 to _fanCount - 1). An out-of-range index could cause undefined behavior with the hardware P/Invoke calls.

@kilo-code-bot

kilo-code-bot Bot commented Mar 20, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 3
SUGGESTION 0
Issue Details (click to expand)

CRITICAL

File Line Issue
AsusFanControl.Core/AsusControl.cs 49 Instance count leak on exception - If HealthyTable_FanCounts() throws after _instanceCount++, the count remains elevated causing subsequent instances to skip initialization

WARNING

File Line Issue
AsusFanControl.Core/AsusControl.cs 145 Silent exception swallowing - If ShutdownWinIo() throws during dispose, the exception is caught and logged with Debug.WriteLine but not re-thrown. This could mask serious hardware failures during shutdown.
AsusFanControl.Core/AsusControl.cs 230 Missing bounds check on fanIndex in SetFanSpeed - could cause undefined hardware behavior
AsusFanControl.Core/AsusControl.cs 276 Missing bounds check on fanIndex in GetFanSpeed - could cause undefined hardware behavior
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
AsusFanControl.Core/FanCurve.cs 87 GetTargetSpeed throws InvalidOperationException if temperature falls outside all point ranges - unexpected behavior for callers
AsusFanControl.Core/FanCurve.cs 197-200 ClonePoint does not validate Temperature/Speed values are in valid ranges
AsusFanControl.Core/AsusControl.cs 283-287 ResetToDefaultAsync returns Task.CompletedTask synchronously - API name is misleading
Files Reviewed (6 files)
  • AsusFanControl.Core/AsusControl.cs - 4 issues
  • AsusFanControl.Core/FanCurve.cs - addressed in prior comments
  • AsusFanControl.Core/IFanController.cs
  • AsusFanControlGUI/FanCurveControl.cs
  • AsusFanControlGUI/FanCurveEditor.cs
  • AsusFanControlGUI/Form1.cs

Reviewed by minimax-m2.5-20260211 · 189,149 tokens

- Make Points property private with thread-safe getter returning a copy
- Add thread-safe APIs: SetPoints, AddPoint, RemovePointAt, UpdatePointAt, PointCount, ClearPoints
- All internal operations now lock properly to prevent concurrent modification
@beenycool
beenycool force-pushed the fix/fancurve-threadsafety branch from 5f2f419 to 92f5c01 Compare March 20, 2026 14:37
@beenycool
beenycool merged commit 5427927 into master Mar 20, 2026
1 of 2 checks passed
@beenycool
beenycool deleted the fix/fancurve-threadsafety branch March 20, 2026 14:38
{
Trace.TraceError($"[AsusControl] Error shutting down WinIo: {ex}");
Debug.WriteLine($"[AsusControl] Error shutting down WinIo: {ex.Message}");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Silent exception swallowing - If ShutdownWinIo() throws during dispose, the exception is caught and logged with Debug.WriteLine but not re-thrown. This could mask serious hardware failures during shutdown. Consider logging at error level or re-throwing if shutdown failure is critical.

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