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
@@ -104,6 +105,11 @@ def ready_worker(worker, last_message)
104105 synchronize { ns_ready_worker worker , last_message }
105106 end
106107
108+ # @!visibility private
109+ def remove_ready_worker ( worker )
110+ synchronize { ns_remove_ready_worker worker }
111+ end
112+
107113 # @!visibility private
108114 def worker_died ( worker )
109115 synchronize { ns_worker_died worker }
@@ -114,9 +120,9 @@ def worker_task_completed
114120 synchronize { @completed_task_count += 1 }
115121 end
116122
117- # @!macro thread_pool_executor_method_prune_pool
118- def prune_pool
119- synchronize { ns_prune_pool }
123+ # @!visibility private
124+ def prunable_capacity
125+ synchronize { ns_prunable_capacity }
120126 end
121127
122128 private
@@ -146,9 +152,6 @@ def ns_initialize(opts)
146152 @largest_length = 0
147153 @workers_counter = 0
148154 @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
152155 end
153156
154157 # @!visibility private
@@ -162,12 +165,10 @@ def ns_execute(*args, &task)
162165
163166 if ns_assign_worker ( *args , &task ) || ns_enqueue ( *args , &task )
164167 @scheduled_task_count += 1
168+ nil
165169 else
166- return fallback_action ( *args , &task )
170+ fallback_action ( *args , &task )
167171 end
168-
169- ns_prune_pool if @next_gc_time < Concurrent . monotonic_time
170- nil
171172 end
172173
173174 # @!visibility private
@@ -218,7 +219,7 @@ def ns_assign_worker(*args, &task)
218219 # @!visibility private
219220 def ns_enqueue ( *args , &task )
220221 return false if @synchronous
221-
222+
222223 if !ns_limited_queue? || @queue . size < @max_queue
223224 @queue << [ task , args ]
224225 true
@@ -265,7 +266,7 @@ def ns_ready_worker(worker, last_message, success = true)
265266 end
266267 end
267268
268- # removes a worker which is not in not tracked in @ready
269+ # removes a worker which is not tracked in @ready
269270 #
270271 # @!visibility private
271272 def ns_remove_busy_worker ( worker )
@@ -274,23 +275,15 @@ def ns_remove_busy_worker(worker)
274275 true
275276 end
276277
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
278+ def ns_remove_ready_worker ( worker )
279+ if index = @ready . index { |rw , _ | rw == worker }
280+ @ready . delete_at ( index )
291281 end
282+ true
283+ end
292284
293- @next_gc_time = Concurrent . monotonic_time + @gc_interval
285+ def ns_prunable_capacity
286+ [ @pool . size - @min_length , @ready . size ] . min
294287 end
295288
296289 def ns_reset_if_forked
@@ -312,7 +305,7 @@ class Worker
312305
313306 def initialize ( pool , id )
314307 # instance variables accessed only under pool's lock so no need to sync here again
315- @queue = Queue . new
308+ @queue = Collection :: TimeoutQueue . new
316309 @pool = pool
317310 @thread = create_worker @queue , pool , pool . idletime
318311
@@ -338,17 +331,29 @@ def kill
338331 def create_worker ( queue , pool , idletime )
339332 Thread . new ( queue , pool , idletime ) do |my_queue , my_pool , my_idletime |
340333 catch ( :stop ) do
341- loop do
334+ not_prunable = false
342335
343- case message = my_queue . pop
336+ loop do
337+ timeout = not_prunable ? nil : my_idletime
338+ case message = my_queue . pop ( timeout : timeout )
339+ when nil
340+ unless my_pool . prunable_capacity . positive?
341+ not_prunable = true
342+ next
343+ end
344+
345+ my_pool . remove_ready_worker ( self )
346+ my_pool . remove_busy_worker ( self )
347+ throw :stop
344348 when :stop
349+ my_pool . remove_ready_worker ( self )
345350 my_pool . remove_busy_worker ( self )
346351 throw :stop
347-
348352 else
349353 task , args = message
350354 run_task my_pool , task , args
351355 my_pool . ready_worker ( self , Concurrent . monotonic_time )
356+ not_prunable = false
352357 end
353358 end
354359 end
0 commit comments