diff --git a/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb b/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb index 4de512a5f..bc661d0a8 100644 --- a/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +++ b/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb @@ -39,6 +39,10 @@ module Concurrent # The number of tasks that have been completed by the pool since construction. # @return [Integer] The number of tasks that have been completed by the pool since construction. + # @!macro thread_pool_executor_attr_reader_available_worker_count + # The number of worker threads that are available to process tasks (either idle or uncreated) + # @return [Integer] The number of worker threads that are available to process tasks (either idle or uncreated) + # @!macro thread_pool_executor_attr_reader_idletime # The number of seconds that a thread may be idle before being reclaimed. # @return [Integer] The number of seconds that a thread may be idle before being reclaimed. diff --git a/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb b/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb index 1213a95fb..696bc4706 100644 --- a/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +++ b/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb @@ -73,6 +73,11 @@ def completed_task_count @executor.getCompletedTaskCount end + # @!macro thread_pool_executor_attr_reader_available_worker_count + def available_worker_count + @executor.getMaximumPoolSize - @executor.getActiveCount + end + # @!macro thread_pool_executor_attr_reader_idletime def idletime @executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS) diff --git a/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb b/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb index 298dd7fed..2411d1925 100644 --- a/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +++ b/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb @@ -61,6 +61,15 @@ def completed_task_count synchronize { @completed_task_count } end + # @!macro thread_pool_executor_attr_reader_available_worker_count + def available_worker_count + synchronize do + uncreated_workers = @max_length - @pool.length + idle_workers = @ready.length + uncreated_workers + idle_workers + end + end + # @!macro executor_service_method_can_overflow_question def can_overflow? synchronize { ns_limited_queue? } diff --git a/spec/concurrent/executor/thread_pool_executor_shared.rb b/spec/concurrent/executor/thread_pool_executor_shared.rb index bb91b3d4b..a82316c9e 100644 --- a/spec/concurrent/executor/thread_pool_executor_shared.rb +++ b/spec/concurrent/executor/thread_pool_executor_shared.rb @@ -258,6 +258,27 @@ end end + context '#available_worker_count' do + subject do + described_class.new( + min_threads: 10, + max_threads: 20, + idletime: 60, + max_queue: 0, + fallback_policy: :discard + ) + end + + it 'returns the number of available (ready/idle or uncreated) workers' do + expect(subject.available_worker_count).to eq 20 + latch = Concurrent::CountDownLatch.new(10) + 10.times{ subject.post{ sleep 0.1; latch.count_down } } + expect(subject.available_worker_count).to eq 10 + expect(latch.wait(1)).to be_truthy + expect(subject.available_worker_count).to eq 20 + end + end + context '#fallback_policy' do let!(:min_threads){ 1 } @@ -642,18 +663,18 @@ caller_runs_thread = Thread.new { executor << proc { executor_unblocker.wait; log.push :unblocked } } - + # Wait until the caller_runs job is blocked Thread.pass until caller_runs_thread.status == 'sleep' - + # Now unblock the worker thread worker_unblocker.count_down queue_done.wait executor_unblocker.count_down - + # Tidy up caller_runs_thread.join - + # We will see the queued jobs run before the caller_runs job unblocks expect([log.pop, log.pop]).to eq [:queued, :unblocked] end