|
3 | 3 |
|
4 | 4 | import json
|
5 | 5 | import logging
|
| 6 | +import math |
6 | 7 | import multiprocessing
|
7 | 8 | import multiprocessing.pool
|
8 | 9 | import os
|
|
12 | 13 | import warnings
|
13 | 14 | from dataclasses import dataclass
|
14 | 15 | from fnmatch import fnmatch
|
| 16 | +from functools import cache |
15 | 17 | from pathlib import Path
|
16 | 18 | from typing import TYPE_CHECKING, Any
|
17 | 19 |
|
@@ -250,7 +252,7 @@ def worker(lintable: Lintable) -> list[MatchError]:
|
250 | 252 | # pylint: disable=unused-variable
|
251 | 253 | global_resource = multiprocessing.Semaphore() # noqa: F841
|
252 | 254 |
|
253 |
| - pool = multiprocessing.pool.ThreadPool(processes=multiprocessing.cpu_count()) |
| 255 | + pool = multiprocessing.pool.ThreadPool(processes=threads()) |
254 | 256 | return_list = pool.map(worker, files, chunksize=1)
|
255 | 257 | pool.close()
|
256 | 258 | pool.join()
|
@@ -534,6 +536,55 @@ def play_children(
|
534 | 536 | return []
|
535 | 537 |
|
536 | 538 |
|
| 539 | +@cache |
| 540 | +def threads() -> int: |
| 541 | + """Determine how many threads to use. |
| 542 | +
|
| 543 | + Inside containers we want to respect limits imposed. |
| 544 | +
|
| 545 | + When present /sys/fs/cgroup/cpu.max can contain something like: |
| 546 | + $ podman/docker run -it --rm --cpus 1.5 ubuntu:latest cat /sys/fs/cgroup/cpu.max |
| 547 | + 150000 100000 |
| 548 | + # "max 100000" is returned when no limits are set. |
| 549 | +
|
| 550 | + See: https://github.com/python/cpython/issues/80235 |
| 551 | + See: https://github.com/python/cpython/issues/70879 |
| 552 | + """ |
| 553 | + os_cpu_count = multiprocessing.cpu_count() |
| 554 | + # Cgroup CPU bandwidth limit available in Linux since 2.6 kernel |
| 555 | + |
| 556 | + cpu_max_fname = "/sys/fs/cgroup/cpu.max" |
| 557 | + cfs_quota_fname = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us" |
| 558 | + cfs_period_fname = "/sys/fs/cgroup/cpu/cpu.cfs_period_us" |
| 559 | + if os.path.exists(cpu_max_fname): |
| 560 | + # cgroup v2 |
| 561 | + # https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html |
| 562 | + with open(cpu_max_fname, encoding="utf-8") as fh: |
| 563 | + cpu_quota_us, cpu_period_us = fh.read().strip().split() |
| 564 | + elif os.path.exists(cfs_quota_fname) and os.path.exists(cfs_period_fname): |
| 565 | + # cgroup v1 |
| 566 | + # https://www.kernel.org/doc/html/latest/scheduler/sched-bwc.html#management |
| 567 | + with open(cfs_quota_fname, encoding="utf-8") as fh: |
| 568 | + cpu_quota_us = fh.read().strip() |
| 569 | + with open(cfs_period_fname, encoding="utf-8") as fh: |
| 570 | + cpu_period_us = fh.read().strip() |
| 571 | + else: |
| 572 | + # No Cgroup CPU bandwidth limit (e.g. non-Linux platform) |
| 573 | + cpu_quota_us = "max" |
| 574 | + cpu_period_us = "100000" # unused, for consistency with default values |
| 575 | + |
| 576 | + if cpu_quota_us == "max": |
| 577 | + # No active Cgroup quota on a Cgroup-capable platform |
| 578 | + return os_cpu_count |
| 579 | + cpu_quota_us_int = int(cpu_quota_us) |
| 580 | + cpu_period_us_int = int(cpu_period_us) |
| 581 | + if cpu_quota_us_int > 0 and cpu_period_us_int > 0: |
| 582 | + return math.ceil(cpu_quota_us_int / cpu_period_us_int) |
| 583 | + # Setting a negative cpu_quota_us value is a valid way to disable |
| 584 | + # cgroup CPU bandwidth limits |
| 585 | + return os_cpu_count |
| 586 | + |
| 587 | + |
537 | 588 | def _get_matches(rules: RulesCollection, options: Options) -> LintResult:
|
538 | 589 | lintables = ansiblelint.utils.get_lintables(opts=options, args=options.lintables)
|
539 | 590 |
|
|
0 commit comments