Skip to content

Commit 947aebf

Browse files
committed
Add Concurrent.cpu_shares that is cgroups aware.
1 parent cadc8de commit 947aebf

File tree

2 files changed

+49
-0
lines changed

2 files changed

+49
-0
lines changed

lib/concurrent-ruby/concurrent/utility/processor_counter.rb

+27
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def initialize
1212
@processor_count = Delay.new { compute_processor_count }
1313
@physical_processor_count = Delay.new { compute_physical_processor_count }
1414
@cpu_quota = Delay.new { compute_cpu_quota }
15+
@cpu_shares = Delay.new { compute_cpu_shares }
1516
end
1617

1718
def processor_count
@@ -41,6 +42,10 @@ def cpu_quota
4142
@cpu_quota.value
4243
end
4344

45+
def cpu_shares
46+
@cpu_shares.value
47+
end
48+
4449
private
4550

4651
def compute_processor_count
@@ -113,6 +118,20 @@ def compute_cpu_quota
113118
end
114119
end
115120
end
121+
122+
def compute_cpu_shares
123+
if RbConfig::CONFIG["target_os"].include?("linux")
124+
if File.exist?("/sys/fs/cgroup/cpu.weight")
125+
# cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files
126+
# Ref: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2254-cgroup-v2#phase-1-convert-from-cgroups-v1-settings-to-v2
127+
weight = File.read("/sys/fs/cgroup/cpu.weight").to_f
128+
((((weight - 1) * 262142) / 9999) + 2) / 1024
129+
elsif File.exist?("/sys/fs/cgroup/cpu/cpu.shares")
130+
# cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt
131+
File.read("/sys/fs/cgroup/cpu/cpu.shares").to_f / 1024
132+
end
133+
end
134+
end
116135
end
117136
end
118137

@@ -187,4 +206,12 @@ def self.available_processor_count
187206
def self.cpu_quota
188207
processor_counter.cpu_quota
189208
end
209+
210+
# The CPU shares requested by the process. For performance reasons the calculated
211+
# value will be memoized on the first call.
212+
#
213+
# @return [Float, nil] CPU shares requested by the process, or nil if not set
214+
def self.cpu_shares
215+
processor_counter.cpu_shares
216+
end
190217
end

spec/concurrent/utility/processor_count_spec.rb

+22
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,26 @@ module Concurrent
9292
end
9393

9494
end
95+
96+
RSpec.describe '#cpu_shares' do
97+
let(:counter) { Concurrent::Utility::ProcessorCounter.new }
98+
99+
it 'returns a float when cgroups v2 sets a cpu.weight' do
100+
expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
101+
expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.weight").and_return(true)
102+
103+
expect(File).to receive(:read).with("/sys/fs/cgroup/cpu.weight").and_return("10000\n")
104+
expect(counter.cpu_shares).to be == 256.0
105+
end
106+
107+
it 'returns a float if cgroups v1 sets a cpu.shares' do
108+
expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux")
109+
expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.weight").and_return(false)
110+
expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu/cpu.shares").and_return(true)
111+
112+
expect(File).to receive(:read).with("/sys/fs/cgroup/cpu/cpu.shares").and_return("512\n")
113+
expect(counter.cpu_shares).to be == 0.5
114+
end
115+
116+
end
95117
end

0 commit comments

Comments
 (0)