33require 'concurrent/concern/logging'
44require 'concurrent/executor/ruby_executor_service'
55require 'concurrent/utility/monotonic_time'
6+ require 'concurrent/collection/timeout_queue'
67
78module Concurrent
89
@@ -95,8 +96,16 @@ def remaining_capacity
9596 end
9697
9798 # @!visibility private
98- def remove_busy_worker ( worker )
99- synchronize { ns_remove_busy_worker worker }
99+ def prunable_capacity
100+ synchronize { ns_prunable_capacity }
101+ end
102+
103+ # @!visibility private
104+ def remove_worker ( worker )
105+ synchronize do
106+ ns_remove_ready_worker worker
107+ ns_remove_busy_worker worker
108+ end
100109 end
101110
102111 # @!visibility private
@@ -114,11 +123,6 @@ def worker_task_completed
114123 synchronize { @completed_task_count += 1 }
115124 end
116125
117- # @!macro thread_pool_executor_method_prune_pool
118- def prune_pool
119- synchronize { ns_prune_pool }
120- end
121-
122126 private
123127
124128 # @!visibility private
@@ -146,9 +150,6 @@ def ns_initialize(opts)
146150 @largest_length = 0
147151 @workers_counter = 0
148152 @ruby_pid = $$ # detects if Ruby has forked
149-
150- @gc_interval = opts . fetch ( :gc_interval , @idletime / 2.0 ) . to_i # undocumented
151- @next_gc_time = Concurrent . monotonic_time + @gc_interval
152153 end
153154
154155 # @!visibility private
@@ -162,12 +163,10 @@ def ns_execute(*args, &task)
162163
163164 if ns_assign_worker ( *args , &task ) || ns_enqueue ( *args , &task )
164165 @scheduled_task_count += 1
166+ nil
165167 else
166- return fallback_action ( *args , &task )
168+ fallback_action ( *args , &task )
167169 end
168-
169- ns_prune_pool if @next_gc_time < Concurrent . monotonic_time
170- nil
171170 end
172171
173172 # @!visibility private
@@ -218,7 +217,7 @@ def ns_assign_worker(*args, &task)
218217 # @!visibility private
219218 def ns_enqueue ( *args , &task )
220219 return false if @synchronous
221-
220+
222221 if !ns_limited_queue? || @queue . size < @max_queue
223222 @queue << [ task , args ]
224223 true
@@ -265,7 +264,7 @@ def ns_ready_worker(worker, last_message, success = true)
265264 end
266265 end
267266
268- # removes a worker which is not in not tracked in @ready
267+ # removes a worker which is not tracked in @ready
269268 #
270269 # @!visibility private
271270 def ns_remove_busy_worker ( worker )
@@ -274,23 +273,19 @@ def ns_remove_busy_worker(worker)
274273 true
275274 end
276275
277- # try oldest worker if it is idle for enough time, it's returned back at the start
278- #
279- # @!visibility private
280- def ns_prune_pool
281- now = Concurrent . monotonic_time
282- stopped_workers = 0
283- while !@ready . empty? && ( @pool . size - stopped_workers > @min_length )
284- worker , last_message = @ready . first
285- if now - last_message > self . idletime
286- stopped_workers += 1
287- @ready . shift
288- worker << :stop
289- else break
290- end
276+ def ns_remove_ready_worker ( worker )
277+ if index = @ready . index { |rw , _ | rw == worker }
278+ @ready . delete_at ( index )
291279 end
280+ true
281+ end
292282
293- @next_gc_time = Concurrent . monotonic_time + @gc_interval
283+ def ns_prunable_capacity
284+ if running?
285+ [ @pool . size - @min_length , @ready . size ] . min
286+ else
287+ @pool . size
288+ end
294289 end
295290
296291 def ns_reset_if_forked
@@ -312,7 +307,7 @@ class Worker
312307
313308 def initialize ( pool , id )
314309 # instance variables accessed only under pool's lock so no need to sync here again
315- @queue = Queue . new
310+ @queue = Collection :: TimeoutQueue . new
316311 @pool = pool
317312 @thread = create_worker @queue , pool , pool . idletime
318313
@@ -338,17 +333,26 @@ def kill
338333 def create_worker ( queue , pool , idletime )
339334 Thread . new ( queue , pool , idletime ) do |my_queue , my_pool , my_idletime |
340335 catch ( :stop ) do
341- loop do
336+ prunable = true
342337
343- case message = my_queue . pop
338+ loop do
339+ timeout = prunable && my_pool . running? ? my_idletime : nil
340+ case message = my_queue . pop ( timeout : timeout )
341+ when nil
342+ if my_pool . prunable_capacity . positive?
343+ my_pool . remove_worker ( self )
344+ throw :stop
345+ end
346+
347+ prunable = false
344348 when :stop
345- my_pool . remove_busy_worker ( self )
349+ my_pool . remove_worker ( self )
346350 throw :stop
347-
348351 else
349352 task , args = message
350353 run_task my_pool , task , args
351354 my_pool . ready_worker ( self , Concurrent . monotonic_time )
355+ prunable = true
352356 end
353357 end
354358 end
0 commit comments