Skip to content

Add threadCount gauge#98

Merged
czechboy0 merged 5 commits intoapple:mainfrom
adlich:threads
Mar 17, 2026
Merged

Add threadCount gauge#98
czechboy0 merged 5 commits intoapple:mainfrom
adlich:threads

Conversation

@adlich
Copy link
Copy Markdown
Contributor

@adlich adlich commented Feb 24, 2026

Add threadCount gauge w/ Linux and macOS implementations and corresponding tests.

Motivation:

Adds a threadCount gauge to track metrics for the number of threads in the process.

Modifications:

  • Add macOS and Linux implementations of a threadCount gauge
  • On macOS, uses ProcessTaskInfo API
  • On Linux, uses sysfs
  • Zombie threads are counted in both cases (reason: resources are in use, and if not leaked those should be observer to decrement anyways, soon matching what's "runnable", but if not, these alert of the leak)
  • Adds tests, using XCTest to benefit from its default process isolation (harder to test these with Swift Testing)
  • Note that on macOS, tests don't reliably see resource clean-up without even less reliable waits. So test gauge going up and down in Linux, but only going up on macOS.
  • Adds Grafana chart to the Example dashboard

Result:

  • Exports additional threadCount metric

Testing:

$ swift-format lint -r --strict .
$ uname && xcrun swift test --enable-all-traits
Darwin
[...]
Test Suite 'swift-system-metricsPackageTests.xctest' started at 2026-02-23 19:12:57.942. Test Suite 'ThreadCountTests' started at 2026-02-23 19:12:57.942. Test Case '-[SystemMetricsTests.ThreadCountTests test_threadCount]' started. Test Case '-[SystemMetricsTests.ThreadCountTests test_threadCount]' passed (0.000 seconds). Test Suite 'ThreadCountTests' passed at 2026-02-23 19:12:57.943.
         Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.001) seconds
Test Suite 'swift-system-metricsPackageTests.xctest' passed at 2026-02-23 19:12:57.943.
         Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.001) seconds
Test Suite 'All tests' passed at 2026-02-23 19:12:57.943.
         Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.001) seconds
◇ Test run started.
[...]
✔ Test run with 16 tests in 3 suites passed after 2.009 seconds.
$ uname && swift test --enable-all-traits
Linux
[...]
Test Suite 'debug.xctest' started at 2026-02-24 03:15:37.877 Test Suite 'ThreadCountTests' started at 2026-02-24 03:15:37.877 Test Case 'ThreadCountTests.test_threadCount' started at 2026-02-24 03:15:37.877 Test Case 'ThreadCountTests.test_threadCount' passed (0.018 seconds) Test Suite 'ThreadCountTests' passed at 2026-02-24 03:15:37.896
         Executed 1 test, with 0 failures (0 unexpected) in 0.018 (0.018) seconds
Test Suite 'debug.xctest' passed at 2026-02-24 03:15:37.896
         Executed 1 test, with 0 failures (0 unexpected) in 0.018 (0.018) seconds
Test Suite 'All tests' passed at 2026-02-24 03:15:37.896
         Executed 1 test, with 0 failures (0 unexpected) in 0.018 (0.018) seconds
◇ Test run started.
[...]
✔ Test run with 13 tests in 3 suites passed after 1.269 seconds.

1/ Add macOS and Linux implementations of a Thread Count gauge
2/ On macOS, uses `ProcessTaskInfo` API
3/ On Linux, uses `sysfs`
4/ Zombie threads are counted in both cases (reason: resources are in use,
   and if not leaked those should be observer to decrement anyways, soon
   matching what's "runnable", but if not, these alert of the leak)
5/ Adds tests, using XCTest to benefit from its default process isolation
   (harder to test these with Swift Testing)
6/ Note that on macOS, tests don't reliably see resource clean-up without
   even less reliable waits. So test gauge going up and down in Linux, but
   only going up on macOS.

Tests:

$ swift-format lint -r --strict .

$ uname && xcrun swift test --enable-all-traits
Darwin
[...]
Test Suite 'swift-system-metricsPackageTests.xctest' started at 2026-02-23 19:12:57.942.
Test Suite 'ThreadCountTests' started at 2026-02-23 19:12:57.942.
Test Case '-[SystemMetricsTests.ThreadCountTests test_threadCount]' started.
Test Case '-[SystemMetricsTests.ThreadCountTests test_threadCount]' passed (0.000 seconds).
Test Suite 'ThreadCountTests' passed at 2026-02-23 19:12:57.943.
         Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.001) seconds
Test Suite 'swift-system-metricsPackageTests.xctest' passed at 2026-02-23 19:12:57.943.
         Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.001) seconds
Test Suite 'All tests' passed at 2026-02-23 19:12:57.943.
         Executed 1 test, with 0 failures (0 unexpected) in 0.000 (0.001) seconds
◇ Test run started.
[...]
✔ Test run with 16 tests in 3 suites passed after 2.009 seconds.

$ uname && swift test --enable-all-traits
Linux
[...]
Test Suite 'debug.xctest' started at 2026-02-24 03:15:37.877
Test Suite 'ThreadCountTests' started at 2026-02-24 03:15:37.877
Test Case 'ThreadCountTests.test_threadCount' started at 2026-02-24 03:15:37.877
Test Case 'ThreadCountTests.test_threadCount' passed (0.018 seconds)
Test Suite 'ThreadCountTests' passed at 2026-02-24 03:15:37.896
         Executed 1 test, with 0 failures (0 unexpected) in 0.018 (0.018) seconds
Test Suite 'debug.xctest' passed at 2026-02-24 03:15:37.896
         Executed 1 test, with 0 failures (0 unexpected) in 0.018 (0.018) seconds
Test Suite 'All tests' passed at 2026-02-24 03:15:37.896
         Executed 1 test, with 0 failures (0 unexpected) in 0.018 (0.018) seconds
◇ Test run started.
[...]
✔ Test run with 13 tests in 3 suites passed after 1.269 seconds.
@czechboy0
Copy link
Copy Markdown
Contributor

Hi @adlich,

this is a great addition.

Can you please elaborate on a few things:

Thanks 🙂

Comment thread Sources/SystemMetrics/Docs.docc/index.md Outdated
Comment thread Tests/SystemMetricsTests/ThreadCountTests.swift Outdated
1/ Breaking changes. Didn't notice these are package and not internal,
   not sure what's the best approach for keeping up stuff like label
   literals in the intended design or stuff like init deprecation in
   Swift library best-practices. PTAL.
2/ 6.0 breakages: seem to differ from swift-format lint enforced ones,
   cleaned up.
3/ SemVer label: I do not seem to have permission to label.

4/ Removed macOS tests for thread count.
5/ Reworded comments around the added use of XCTest

Across mac/linux, all clean:

```
$ swift-format lint -r --strict .
$ swift test -Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable
$ swift package diagnose-api-breaking-changes main
```
@adlich
Copy link
Copy Markdown
Contributor Author

adlich commented Feb 24, 2026

Thanks for the quick review, @czechboy0 .

why did you post the standard swift-format command when the CONTRIBUTING documents how to verify formatting:

1/ Followed section "How to contribute your work"
2/ Skipped that which was not mandatory ("you can") nor referenced from (1).
3/ For this contribution which is incidental to what I was working on, it makes more sense to manually perform the expected checks than to review what is behind the scripts/etc that I'd end up running locally with act. It may change if I spend more time in this project.

What do you mean by "using XCTest to benefit from its default process isolation

That with the default serial execution behavior, we "for free" get the desired process isolation for those tests at this moment (vs not having control over parallelization across separate Swift Testing suites). Reworded for clarity.

was any part of this written by an LLM or did you write it all?

I wrote it all. This perhaps could be added to the PR template.

Missed staging... just protecting #import FoundationEssentials
regardless of being in conditionally compiled code for glibc/musl.

Tests:

On mac and linux, clean:

```
$ swift-format lint -r --strict .
$ swift test -Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable
```
Comment thread Sources/SystemMetrics/SystemMetricsMonitor.swift Outdated
Comment thread Sources/SystemMetrics/SystemMetricsMonitorConfiguration.swift Outdated
Comment thread Tests/SystemMetricsTests/ThreadCountTests.swift Outdated
Tests:

$ swift-format lint -r --strict .
$ swift test -Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable
✔ Test run with 13 tests passed after 1.627 seconds.
Comment thread Tests/SystemMetricsTests/LinuxDataProviderTests.swift
@kukushechkin kukushechkin added the 🆕 semver/minor Adds new public API. label Mar 3, 2026
1/ Remove the OS &run-isolation dependent thread tests
2/ Add missing basic test per feedback

*Tests*:

On MacOS and Linux.

$ swift-format lint -r --strict .

$ swift test -Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable
[...]
✔ Test run with 16 tests in 3 suites passed after 2.013 seconds.

$ swift package diagnose-api-breaking-changes main

2 breaking changes detected in SystemMetrics:
  💔 API breakage: constructor SystemMetricsMonitor.Data.init(virtualMemoryBytes:residentMemoryBytes:startTimeSeconds:cpuSeconds:maxFileDescriptors:openFileDescriptors:) has been removed
  💔 API breakage: constructor SystemMetricsMonitor.Configuration.Labels.init(prefix:virtualMemoryBytes:residentMemoryBytes:startTimeSeconds:cpuSecondsTotal:maxFileDescriptors:openFileDescriptors:) has been removed

*Note*: Only the expected ones, to be overridden to merge.
Comment thread Sources/SystemMetrics/SystemMetricsMonitor.swift
@kukushechkin kukushechkin enabled auto-merge (squash) March 17, 2026 12:22
@czechboy0 czechboy0 disabled auto-merge March 17, 2026 12:38
@czechboy0 czechboy0 merged commit 758865b into apple:main Mar 17, 2026
33 of 34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆕 semver/minor Adds new public API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants