Skip to content

[Correctness] Manager PerformanceViewModel.cs — ProcessHelper calls made without _metricsLock held, inconsistent with MainViewModel #799

@Christophe-Rogiers

Description

@Christophe-Rogiers

Severity: Warning

Files:

  • src/Servy.Manager/ViewModels/PerformanceViewModel.cs lines 230-234
  • src/Servy.Manager/ViewModels/MainViewModel.cs lines 969-975

Description:

MainViewModel serializes ProcessHelper.MaintainCache() + GetProcessTreeMetrics() calls under _metricsLock:

// MainViewModel.cs:969-975
lock (_metricsLock)
{
    ProcessHelper.MaintainCache();
    var metrics = ProcessHelper.GetProcessTreeMetrics(targetPid.Value);
    cpu = metrics.CpuUsage;
    ram = metrics.RamUsage;
}

PerformanceViewModel calls the same two methods with no lock:

// PerformanceViewModel.cs:230-234
var processMetrics = await Task.Run(() =>
{
    ProcessHelper.MaintainCache();
    return ProcessHelper.GetProcessTreeMetrics(pid);
});

Both view models run on their own background ticks and can therefore have MaintainCache and GetProcessTreeMetrics interleaved on the shared static ProcessHelper.PrevCpuTimes cache. Even though PrevCpuTimes is a ConcurrentDictionary, the CPU-delta computation (read previous sample → compute delta → write new sample) is a compound read-modify-write. Concurrent threads for the same PID can produce incorrect deltas (double-counted kernel time, or the CPU% glitches already flagged in #796).

Only one of the two patterns is correct — either the lock in MainViewModel is superfluous, or it is necessary and PerformanceViewModel is missing it.

Suggested fix:

Decide the contract at the source:

  • If the lock is required (most likely, given the RMW on PrevCpuTimes): move it into ProcessHelper.GetProcessTreeMetrics itself (one place, no caller has to remember). Remove the caller-side locks from both view models.
  • If it is not required: remove the lock from MainViewModel to stop false-serialization of unrelated PIDs.

Either way the two call sites should match, and ProcessHelper's thread-safety contract should be documented in XML-doc comments on the static methods so future callers don't have to infer it from one of two inconsistent usages.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions