From 606f443e3263ed9d495ad3bc30fd7f10ea41b7f0 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Tue, 16 Jan 2024 12:19:12 +0100 Subject: [PATCH 01/15] CI: Tell dependabot to update GH Actions --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5ace4600a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From 95a53a5d9a626b6d3cfc0cb18a3ae501806ee65a Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 16 Jan 2024 12:27:21 +0100 Subject: [PATCH 02/15] Fix link label in signpost.md --- docs-source/signpost.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-source/signpost.md b/docs-source/signpost.md index 8f9bb7b1a..4bf140769 100644 --- a/docs-source/signpost.md +++ b/docs-source/signpost.md @@ -3,7 +3,7 @@ Pick a `concurrent-ruby` version: * [master](./master/index.html) -* [1.2.0 with edge 0.7.0](./1.2.3/index.html) +* [1.2.3 with edge 0.7.0](./1.2.3/index.html) * [1.1.10 with edge 0.6.0](./1.1.10/index.html) * [1.1.9 with edge 0.6.0](./1.1.9/index.html) * [1.1.8 with edge 0.6.0](./1.1.8/index.html) From e97376a5ea6982a5bee5f1bc608614c03b596e66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:25:56 +0000 Subject: [PATCH 03/15] Bump actions/upload-pages-artifact from 1 to 3 Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 1 to 3. - [Release notes](https://github.com/actions/upload-pages-artifact/releases) - [Commits](https://github.com/actions/upload-pages-artifact/compare/v1...v3) --- updated-dependencies: - dependency-name: actions/upload-pages-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 34dab01fd..e744eb614 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,7 +29,7 @@ jobs: - run: ruby support/generate_docs.rb - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: path: docs From 5483ac780cdb0b717e63862a30304d3b70ab00e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:26:20 +0000 Subject: [PATCH 04/15] Bump actions/deploy-pages from 1 to 4 Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 1 to 4. - [Release notes](https://github.com/actions/deploy-pages/releases) - [Commits](https://github.com/actions/deploy-pages/compare/v1...v4) --- updated-dependencies: - dependency-name: actions/deploy-pages dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e744eb614..871eeec96 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -45,4 +45,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 + uses: actions/deploy-pages@v4 From 179d068daa55550132a315daaac5f138b4106dd4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:26:08 +0000 Subject: [PATCH 05/15] Bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- .github/workflows/docs.yml | 2 +- .github/workflows/experimental.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8755f249..73742269a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: name: "Tests: Ruby ${{ matrix.ruby }}" steps: - name: Clone Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 with: @@ -40,7 +40,7 @@ jobs: env: RUBYOPT: '-w' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 871eeec96..780f0741c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,7 +18,7 @@ jobs: env: BUNDLE_WITH: "documentation" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml index 6e469a4a1..724c2640c 100644 --- a/.github/workflows/experimental.yml +++ b/.github/workflows/experimental.yml @@ -22,7 +22,7 @@ jobs: name: "Tests: Experimental Ruby ${{ matrix.ruby }}" steps: - name: Clone Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Ruby ${{ matrix.ruby }} uses: ruby/setup-ruby@v1 with: From 2146c348470e12b92222c860ca3872f36dc981e1 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 18 Jan 2024 18:14:58 +0100 Subject: [PATCH 06/15] No continue-on-error for head Rubies * It is already a daily job so that would just hide failures needlessly. --- .github/workflows/experimental.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml index 724c2640c..be9e387ec 100644 --- a/.github/workflows/experimental.yml +++ b/.github/workflows/experimental.yml @@ -3,12 +3,10 @@ on: schedule: - cron: '0 0 * * *' # Runs every day at midnight workflow_dispatch: - branches: [ master ] jobs: build: runs-on: ubuntu-latest - continue-on-error: true strategy: matrix: From 4a037aeb4994bd716021434a87297c1868b374c7 Mon Sep 17 00:00:00 2001 From: Kevin Kohrt Date: Wed, 24 Jan 2024 05:51:11 -0600 Subject: [PATCH 07/15] Advance "latest" ruby in isolated ci test job to 3.3 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73742269a..5bc05a840 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ 2.3, 3.2 ] # oldest and latest CRuby + ruby: [ 2.3, 3.3 ] # oldest and latest CRuby env: RUBYOPT: '-w' steps: From e9748aff41e79d6b218c2ed528c20da617d0f2a2 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 24 Jan 2024 13:31:01 +0100 Subject: [PATCH 08/15] Use 'ruby' for dynamic latest CRuby release --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bc05a840..b6152f01d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ 2.3, 3.3 ] # oldest and latest CRuby + ruby: [ 2.3, ruby ] # oldest and latest CRuby env: RUBYOPT: '-w' steps: From a5f09a5c7d0478c849d904f285d8b6dd3d46021f Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 29 Jan 2024 16:50:37 +0100 Subject: [PATCH 09/15] Add Concurrent.usable_processor_count that is cgroups aware Closes: https://github.com/ruby-concurrency/concurrent-ruby/pull/1035 A running gag since the introduction of containerization is software that starts one process per logical or physical core while running inside a container with a restricted CPU quota and totally blowing up memory usage in containerized environments. The proper question to ask is how many CPU cores are usable, not how many the machine has. To do that we have to read the cgroup info from `/sys`. There is two way of doing it depending on the version of cgroups used. Co-Authored-By: usiegl00 <50933431+usiegl00@users.noreply.github.com> --- .../concurrent/utility/processor_counter.rb | 65 ++++++++++++++++ .../utility/processor_count_spec.rb | 75 +++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb index 986e2d523..5a1d82be3 100644 --- a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +++ b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb @@ -11,6 +11,7 @@ class ProcessorCounter def initialize @processor_count = Delay.new { compute_processor_count } @physical_processor_count = Delay.new { compute_physical_processor_count } + @cpu_quota = Delay.new { compute_cpu_quota } end def processor_count @@ -21,6 +22,25 @@ def physical_processor_count @physical_processor_count.value end + def available_processor_count + cpu_count = processor_count.to_f + quota = cpu_quota + + return cpu_count if quota.nil? + + # cgroup cpus quotas have no limits, so they can be set to higher than the + # real count of cores. + if quota > cpu_count + cpu_count + else + quota + end + end + + def cpu_quota + @cpu_quota.value + end + private def compute_processor_count @@ -60,6 +80,24 @@ def compute_physical_processor_count rescue return 1 end + + def compute_cpu_quota + if RbConfig::CONFIG["target_os"].match(/linux/i) + if File.exist?("/sys/fs/cgroup/cpu.max") + # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files + cpu_max = File.read("/sys/fs/cgroup/cpu.max") + return nil if cpu_max.start_with?("max ") # no limit + max, period = cpu_max.split.map(&:to_f) + max / period + elsif File.exist?("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us") + # cgroups v1: https://kernel.googlesource.com/pub/scm/linux/kernel/git/glommer/memcg/+/cpu_stat/Documentation/cgroups/cpu.txt + max = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").to_i + return nil if max == 0 + period = File.read("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").to_f + max / period + end + end + end end end @@ -107,4 +145,31 @@ def self.processor_count def self.physical_processor_count processor_counter.physical_processor_count end + + # Number of processors cores available for process scheduling. + # Returns `nil` if there is no #cpu_quota, or a `Float` if the + # process is inside a cgroup with a dedicated CPU quota (typically Docker). + # + # For performance reasons the calculated value will be memoized on the first + # call. + # + # @return [nil, Float] number of available processors seen by the OS or Java runtime + def self.available_processor_count + processor_counter.available_processor_count + end + + # The maximun number of processors cores available for process scheduling. + # Returns `nil` if there is no enforced limit, or a `Float` if the + # process is inside a cgroup with a dedicated CPU quota (typically Docker). + # + # Note that nothing prevent to set a CPU quota higher than the actual number of + # cores on the system. + # + # For performance reasons the calculated value will be memoized on the first + # call. + # + # @return [nil, Float] Maximum number of available processors seen by the OS or Java runtime + def self.cpu_quota + processor_counter.cpu_quota + end end diff --git a/spec/concurrent/utility/processor_count_spec.rb b/spec/concurrent/utility/processor_count_spec.rb index 229125feb..1692dc988 100644 --- a/spec/concurrent/utility/processor_count_spec.rb +++ b/spec/concurrent/utility/processor_count_spec.rb @@ -17,4 +17,79 @@ module Concurrent expect(Concurrent::physical_processor_count).to be >= 1 end end + + RSpec.describe '#cpu_quota' do + + let(:counter) { Concurrent::Utility::ProcessorCounter.new } + + it 'returns #compute_cpu_quota' do + expect(Concurrent::cpu_quota).to be == counter.cpu_quota + end + + it 'returns nil if no quota is detected' do + if RbConfig::CONFIG["target_os"].match(/linux/i) + expect(File).to receive(:exist?).twice.and_return(nil) # Checks for cgroups V1 and V2 + end + expect(counter.cpu_quota).to be_nil + end + + it 'returns nil if cgroups v2 sets no limit' do + expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux") + expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(true) + expect(File).to receive(:read).with("/sys/fs/cgroup/cpu.max").and_return("max 100000\n") + expect(counter.cpu_quota).to be_nil + end + + it 'returns a float if cgroups v2 sets a limit' do + expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux") + expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(true) + expect(File).to receive(:read).with("/sys/fs/cgroup/cpu.max").and_return("150000 100000\n") + expect(counter.cpu_quota).to be == 1.5 + end + + it 'returns nil if cgroups v1 sets no limit' do + expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux") + expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(false) + expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return(true) + + expect(File).to receive(:read).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return("max\n") + expect(counter.cpu_quota).to be_nil + end + + it 'returns a float if cgroups v1 sets a limit' do + expect(RbConfig::CONFIG).to receive(:[]).with("target_os").and_return("linux") + expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu.max").and_return(false) + expect(File).to receive(:exist?).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return(true) + + expect(File).to receive(:read).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us").and_return("150000\n") + expect(File).to receive(:read).with("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us").and_return("100000\n") + expect(counter.cpu_quota).to be == 1.5 + end + + end + + RSpec.describe '#available_processor_count' do + + it 'returns #processor_count if #cpu_quota is nil' do + expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(nil) + available_processor_count = Concurrent::available_processor_count + expect(available_processor_count).to be == Concurrent::processor_count + expect(available_processor_count).to be_a Float + end + + it 'returns #processor_count if #cpu_quota is higher' do + expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(Concurrent::processor_count.to_f * 2) + available_processor_count = Concurrent::available_processor_count + expect(available_processor_count).to be == Concurrent::processor_count + expect(available_processor_count).to be_a Float + end + + it 'returns #cpu_quota if #cpu_quota is lower than #processor_count' do + expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(Concurrent::processor_count.to_f / 2) + available_processor_count = Concurrent::available_processor_count + expect(available_processor_count).to be == Concurrent::processor_count.to_f / 2 + expect(available_processor_count).to be_a Float + end + + end end From fe562f6a99066c50a1a857f35917495719fb1034 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 1 Feb 2024 13:47:08 +0100 Subject: [PATCH 10/15] Cleanups --- .../concurrent/utility/processor_counter.rb | 6 +++--- spec/concurrent/utility/processor_count_spec.rb | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb index 5a1d82be3..2e20210ce 100644 --- a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +++ b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb @@ -82,7 +82,7 @@ def compute_physical_processor_count end def compute_cpu_quota - if RbConfig::CONFIG["target_os"].match(/linux/i) + if RbConfig::CONFIG["target_os"].include?("linux") if File.exist?("/sys/fs/cgroup/cpu.max") # cgroups v2: https://docs.kernel.org/admin-guide/cgroup-v2.html#cpu-interface-files cpu_max = File.read("/sys/fs/cgroup/cpu.max") @@ -158,7 +158,7 @@ def self.available_processor_count processor_counter.available_processor_count end - # The maximun number of processors cores available for process scheduling. + # The maximum number of processors cores available for process scheduling. # Returns `nil` if there is no enforced limit, or a `Float` if the # process is inside a cgroup with a dedicated CPU quota (typically Docker). # @@ -168,7 +168,7 @@ def self.available_processor_count # For performance reasons the calculated value will be memoized on the first # call. # - # @return [nil, Float] Maximum number of available processors seen by the OS or Java runtime + # @return [nil, Float] Maximum number of available processors as set by a cgroup CPU quota, or nil if none set def self.cpu_quota processor_counter.cpu_quota end diff --git a/spec/concurrent/utility/processor_count_spec.rb b/spec/concurrent/utility/processor_count_spec.rb index 1692dc988..fdc44b0ae 100644 --- a/spec/concurrent/utility/processor_count_spec.rb +++ b/spec/concurrent/utility/processor_count_spec.rb @@ -27,7 +27,7 @@ module Concurrent end it 'returns nil if no quota is detected' do - if RbConfig::CONFIG["target_os"].match(/linux/i) + if RbConfig::CONFIG["target_os"].include?("linux") expect(File).to receive(:exist?).twice.and_return(nil) # Checks for cgroups V1 and V2 end expect(counter.cpu_quota).to be_nil @@ -72,21 +72,21 @@ module Concurrent it 'returns #processor_count if #cpu_quota is nil' do expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(nil) - available_processor_count = Concurrent::available_processor_count + available_processor_count = Concurrent.available_processor_count expect(available_processor_count).to be == Concurrent::processor_count expect(available_processor_count).to be_a Float end it 'returns #processor_count if #cpu_quota is higher' do expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(Concurrent::processor_count.to_f * 2) - available_processor_count = Concurrent::available_processor_count + available_processor_count = Concurrent.available_processor_count expect(available_processor_count).to be == Concurrent::processor_count expect(available_processor_count).to be_a Float end it 'returns #cpu_quota if #cpu_quota is lower than #processor_count' do expect(Concurrent::processor_counter).to receive(:cpu_quota).and_return(Concurrent::processor_count.to_f / 2) - available_processor_count = Concurrent::available_processor_count + available_processor_count = Concurrent.available_processor_count expect(available_processor_count).to be == Concurrent::processor_count.to_f / 2 expect(available_processor_count).to be_a Float end From eae2851b53d988ab314030407dd5030f78db5c90 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 1 Feb 2024 13:48:27 +0100 Subject: [PATCH 11/15] Clarify --- lib/concurrent-ruby/concurrent/utility/processor_counter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb index 2e20210ce..c248e6c9f 100644 --- a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +++ b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb @@ -153,7 +153,7 @@ def self.physical_processor_count # For performance reasons the calculated value will be memoized on the first # call. # - # @return [nil, Float] number of available processors seen by the OS or Java runtime + # @return [nil, Float] number of available processors def self.available_processor_count processor_counter.available_processor_count end From 899621f193541c618073cb2bd8770686fb49d2a2 Mon Sep 17 00:00:00 2001 From: "Ben Sheldon [he/him]" Date: Mon, 11 Mar 2024 07:21:29 -0700 Subject: [PATCH 12/15] Add 10 minute timeout to GitHub Actions --- .github/workflows/ci.yml | 2 ++ .github/workflows/experimental.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6152f01d..bb118f400 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ concurrency: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 10 strategy: fail-fast: false @@ -33,6 +34,7 @@ jobs: isolated: name: "Test isolated" runs-on: ubuntu-latest + timeout-minutes: 10 strategy: fail-fast: false matrix: diff --git a/.github/workflows/experimental.yml b/.github/workflows/experimental.yml index be9e387ec..28048747c 100644 --- a/.github/workflows/experimental.yml +++ b/.github/workflows/experimental.yml @@ -7,6 +7,7 @@ on: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 10 strategy: matrix: From 8b9b0da4a37585ce5eb71516aca55e93bde39115 Mon Sep 17 00:00:00 2001 From: "Ben Sheldon [he/him]" Date: Thu, 29 Feb 2024 21:15:25 -0800 Subject: [PATCH 13/15] Align Java Executor Service behavior for `shuttingdown?`, `shutdown?` Co-authored-by: Benoit Daloze --- .../executor/java_executor_service.rb | 8 +--- .../executor/executor_service_shared.rb | 44 ++++++++++++++++++- .../executor/immediate_executor_spec.rb | 2 +- .../indirect_immediate_executor_spec.rb | 2 +- .../executor/serialized_execution_spec.rb | 2 +- 5 files changed, 48 insertions(+), 10 deletions(-) diff --git a/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb b/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb index 7c9ab178e..b2bc69a6e 100644 --- a/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +++ b/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb @@ -57,15 +57,11 @@ def ns_running? end def ns_shuttingdown? - if @executor.respond_to? :isTerminating - @executor.isTerminating - else - false - end + @executor.isShutdown && !@executor.isTerminated end def ns_shutdown? - @executor.isShutdown || @executor.isTerminated + @executor.isTerminated end class Job diff --git a/spec/concurrent/executor/executor_service_shared.rb b/spec/concurrent/executor/executor_service_shared.rb index e5ffa367c..7b87922d5 100644 --- a/spec/concurrent/executor/executor_service_shared.rb +++ b/spec/concurrent/executor/executor_service_shared.rb @@ -3,7 +3,7 @@ require 'concurrent/atomic/atomic_fixnum' require 'timeout' -RSpec.shared_examples :executor_service do +RSpec.shared_examples :executor_service do |immediate_type: false| after(:each) do subject.shutdown @@ -84,6 +84,48 @@ end end + context '#shuttingdown?' do + it 'returns false when the thread pool is running' do + expect(subject).not_to be_shuttingdown + end + + it 'returns true when the thread pool is shutting down' do + skip "will never be in shuttingdown? state" if immediate_type + + subject.post{ sleep(0.5) } + subject.shutdown + expect(subject).to be_shuttingdown + expect(subject.wait_for_termination(pool_termination_timeout)).to eq true + end + + it 'returns false when the thread pool is shutdown' do + subject.shutdown + expect(subject.wait_for_termination(pool_termination_timeout)).to eq true + expect(subject).not_to be_shuttingdown + end + end + + context '#shutdown?' do + it 'returns false when the thread pool is running' do + expect(subject).not_to be_shutdown + end + + it 'returns false when the thread pool is shutting down' do + skip "will never be in shuttingdown? state" if immediate_type + + subject.post{ sleep(0.5) } + subject.shutdown + expect(subject).not_to be_shutdown + expect(subject.wait_for_termination(pool_termination_timeout)).to eq true + end + + it 'returns true when the thread pool is shutdown' do + subject.shutdown + expect(subject.wait_for_termination(pool_termination_timeout)).to eq true + expect(subject).to be_shutdown + end + end + context '#shutdown' do it 'stops accepting new tasks' do diff --git a/spec/concurrent/executor/immediate_executor_spec.rb b/spec/concurrent/executor/immediate_executor_spec.rb index c53efd72e..53f2748ab 100644 --- a/spec/concurrent/executor/immediate_executor_spec.rb +++ b/spec/concurrent/executor/immediate_executor_spec.rb @@ -7,6 +7,6 @@ module Concurrent subject { ImmediateExecutor.new } - it_should_behave_like :executor_service + it_should_behave_like :executor_service, immediate_type: true end end diff --git a/spec/concurrent/executor/indirect_immediate_executor_spec.rb b/spec/concurrent/executor/indirect_immediate_executor_spec.rb index f40364f28..c3c043d73 100644 --- a/spec/concurrent/executor/indirect_immediate_executor_spec.rb +++ b/spec/concurrent/executor/indirect_immediate_executor_spec.rb @@ -7,7 +7,7 @@ module Concurrent subject { IndirectImmediateExecutor.new } - it_should_behave_like :executor_service + it_should_behave_like :executor_service, immediate_type: true it "runs its tasks synchronously" do start = Time.now diff --git a/spec/concurrent/executor/serialized_execution_spec.rb b/spec/concurrent/executor/serialized_execution_spec.rb index 5f0cb4172..11b9bc72c 100644 --- a/spec/concurrent/executor/serialized_execution_spec.rb +++ b/spec/concurrent/executor/serialized_execution_spec.rb @@ -8,6 +8,6 @@ module Concurrent subject { SerializedExecutionDelegator.new(ImmediateExecutor.new) } - it_should_behave_like :executor_service + it_should_behave_like :executor_service, immediate_type: true end end From 83d31a3822722bf679739f0af9704f0062737c7d Mon Sep 17 00:00:00 2001 From: Kevin Menard Date: Tue, 28 May 2024 16:07:35 -0400 Subject: [PATCH 14/15] Fix a small grammar issue. --- lib/concurrent-ruby/concurrent/utility/processor_counter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb index c248e6c9f..e31808722 100644 --- a/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +++ b/lib/concurrent-ruby/concurrent/utility/processor_counter.rb @@ -162,7 +162,7 @@ def self.available_processor_count # Returns `nil` if there is no enforced limit, or a `Float` if the # process is inside a cgroup with a dedicated CPU quota (typically Docker). # - # Note that nothing prevent to set a CPU quota higher than the actual number of + # Note that nothing prevents setting a CPU quota higher than the actual number of # cores on the system. # # For performance reasons the calculated value will be memoized on the first From bda72aeffc1a7663d33014aa887dd3908caa92dd Mon Sep 17 00:00:00 2001 From: Kevin Menard Date: Tue, 28 May 2024 16:07:40 -0400 Subject: [PATCH 15/15] Prepare the 1.3.0 release. --- CHANGELOG.md | 5 +++++ docs-source/signpost.md | 2 +- lib/concurrent-ruby/concurrent/version.rb | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9426af24..89cb50dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Current +## Release v1.3.0 (28 May 2024) + +* (#1042) Align Java Executor Service behavior for `shuttingdown?`, `shutdown?` +* (#1038) Add `Concurrent.usable_processor_count` that is cgroups aware. + ## Release v1.2.3 (16 Jan 2024) * See [the GitHub release](https://github.com/ruby-concurrency/concurrent-ruby/releases/tag/v1.2.3) for details. diff --git a/docs-source/signpost.md b/docs-source/signpost.md index 4bf140769..06c96783a 100644 --- a/docs-source/signpost.md +++ b/docs-source/signpost.md @@ -3,7 +3,7 @@ Pick a `concurrent-ruby` version: * [master](./master/index.html) -* [1.2.3 with edge 0.7.0](./1.2.3/index.html) +* [1.3.0 with edge 0.7.0](./1.2.3/index.html) * [1.1.10 with edge 0.6.0](./1.1.10/index.html) * [1.1.9 with edge 0.6.0](./1.1.9/index.html) * [1.1.8 with edge 0.6.0](./1.1.8/index.html) diff --git a/lib/concurrent-ruby/concurrent/version.rb b/lib/concurrent-ruby/concurrent/version.rb index 9a1c29223..647e7add5 100644 --- a/lib/concurrent-ruby/concurrent/version.rb +++ b/lib/concurrent-ruby/concurrent/version.rb @@ -1,3 +1,3 @@ module Concurrent - VERSION = '1.2.3' + VERSION = '1.3.0' end