concurrent-ruby 0.9.2-java → 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -1
  3. data/README.md +86 -120
  4. data/lib/concurrent.rb +14 -5
  5. data/lib/concurrent/agent.rb +587 -0
  6. data/lib/concurrent/array.rb +39 -0
  7. data/lib/concurrent/async.rb +296 -149
  8. data/lib/concurrent/atom.rb +135 -45
  9. data/lib/concurrent/atomic/abstract_thread_local_var.rb +38 -0
  10. data/lib/concurrent/atomic/atomic_boolean.rb +83 -118
  11. data/lib/concurrent/atomic/atomic_fixnum.rb +101 -163
  12. data/lib/concurrent/atomic/atomic_reference.rb +1 -8
  13. data/lib/concurrent/atomic/count_down_latch.rb +62 -103
  14. data/lib/concurrent/atomic/cyclic_barrier.rb +3 -1
  15. data/lib/concurrent/atomic/event.rb +1 -1
  16. data/lib/concurrent/atomic/java_count_down_latch.rb +39 -0
  17. data/lib/concurrent/atomic/java_thread_local_var.rb +50 -0
  18. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +60 -0
  19. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +91 -0
  20. data/lib/concurrent/atomic/mutex_count_down_latch.rb +43 -0
  21. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  22. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  23. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  24. data/lib/concurrent/atomic/ruby_thread_local_var.rb +172 -0
  25. data/lib/concurrent/atomic/semaphore.rb +84 -178
  26. data/lib/concurrent/atomic/thread_local_var.rb +65 -294
  27. data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
  28. data/lib/concurrent/atomic_reference/jruby.rb +1 -1
  29. data/lib/concurrent/atomic_reference/mutex_atomic.rb +14 -8
  30. data/lib/concurrent/atomic_reference/ruby.rb +1 -1
  31. data/lib/concurrent/atomics.rb +7 -37
  32. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  33. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  34. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  35. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  36. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  37. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +144 -0
  38. data/lib/concurrent/collection/map/synchronized_map_backend.rb +86 -0
  39. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  40. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  41. data/lib/concurrent/concern/dereferenceable.rb +9 -24
  42. data/lib/concurrent/concern/logging.rb +1 -1
  43. data/lib/concurrent/concern/obligation.rb +11 -20
  44. data/lib/concurrent/concern/observable.rb +38 -13
  45. data/lib/concurrent/configuration.rb +23 -152
  46. data/lib/concurrent/constants.rb +8 -0
  47. data/lib/concurrent/delay.rb +14 -12
  48. data/lib/concurrent/exchanger.rb +339 -41
  49. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  50. data/lib/concurrent/executor/executor_service.rb +23 -359
  51. data/lib/concurrent/executor/immediate_executor.rb +3 -2
  52. data/lib/concurrent/executor/java_executor_service.rb +100 -0
  53. data/lib/concurrent/executor/java_single_thread_executor.rb +3 -3
  54. data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -4
  55. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  56. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -66
  57. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +25 -22
  58. data/lib/concurrent/executor/safe_task_executor.rb +6 -7
  59. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  60. data/lib/concurrent/executor/serialized_execution.rb +10 -33
  61. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  62. data/lib/concurrent/executor/simple_executor_service.rb +1 -10
  63. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  64. data/lib/concurrent/executor/timer_set.rb +8 -10
  65. data/lib/concurrent/executors.rb +12 -2
  66. data/lib/concurrent/future.rb +6 -4
  67. data/lib/concurrent/hash.rb +35 -0
  68. data/lib/concurrent/immutable_struct.rb +5 -1
  69. data/lib/concurrent/ivar.rb +12 -16
  70. data/lib/concurrent/lazy_register.rb +11 -8
  71. data/lib/concurrent/map.rb +180 -0
  72. data/lib/concurrent/maybe.rb +6 -3
  73. data/lib/concurrent/mutable_struct.rb +7 -6
  74. data/lib/concurrent/mvar.rb +26 -2
  75. data/lib/concurrent/{executor/executor.rb → options.rb} +5 -29
  76. data/lib/concurrent/promise.rb +7 -5
  77. data/lib/concurrent/scheduled_task.rb +13 -71
  78. data/lib/concurrent/settable_struct.rb +5 -4
  79. data/lib/concurrent/synchronization.rb +15 -3
  80. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  81. data/lib/concurrent/synchronization/abstract_object.rb +7 -146
  82. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  83. data/lib/concurrent/synchronization/condition.rb +6 -4
  84. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  85. data/lib/concurrent/synchronization/jruby_object.rb +44 -0
  86. data/lib/concurrent/synchronization/lock.rb +3 -2
  87. data/lib/concurrent/synchronization/lockable_object.rb +72 -0
  88. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  89. data/lib/concurrent/synchronization/mri_object.rb +43 -0
  90. data/lib/concurrent/synchronization/object.rb +140 -73
  91. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  92. data/lib/concurrent/synchronization/rbx_object.rb +30 -73
  93. data/lib/concurrent/synchronization/volatile.rb +34 -0
  94. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  95. data/lib/concurrent/thread_safe/util.rb +14 -0
  96. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  97. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +30 -0
  98. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  99. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  100. data/lib/concurrent/thread_safe/util/striped64.rb +241 -0
  101. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  102. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  103. data/lib/concurrent/timer_task.rb +3 -4
  104. data/lib/concurrent/tuple.rb +86 -0
  105. data/lib/concurrent/tvar.rb +5 -1
  106. data/lib/concurrent/utility/at_exit.rb +1 -1
  107. data/lib/concurrent/utility/engine.rb +4 -0
  108. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  109. data/lib/concurrent/utility/native_extension_loader.rb +50 -30
  110. data/lib/concurrent/version.rb +2 -2
  111. data/lib/concurrent_ruby_ext.jar +0 -0
  112. metadata +47 -12
  113. data/lib/concurrent/atomic/condition.rb +0 -78
  114. data/lib/concurrent/collection/priority_queue.rb +0 -360
  115. data/lib/concurrent/synchronization/java_object.rb +0 -34
  116. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  117. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
  118. data/lib/concurrent/utilities.rb +0 -5
  119. data/lib/concurrent/utility/timeout.rb +0 -39
  120. data/lib/concurrent/utility/timer.rb +0 -26
  121. data/lib/concurrent_ruby.rb +0 -2
@@ -1,5 +1,6 @@
1
1
  require 'concurrent/atomic/event'
2
- require 'concurrent/executor/executor_service'
2
+ require 'concurrent/executor/abstract_executor_service'
3
+ require 'concurrent/executor/serial_executor_service'
3
4
 
4
5
  module Concurrent
5
6
 
@@ -13,7 +14,7 @@ module Concurrent
13
14
  # during testing because it makes all operations deterministic.
14
15
  #
15
16
  # @note Intended for use primarily in testing and debugging.
16
- class ImmediateExecutor
17
+ class ImmediateExecutor < AbstractExecutorService
17
18
  include SerialExecutorService
18
19
 
19
20
  # Creates a new executor
@@ -0,0 +1,100 @@
1
+ if Concurrent.on_jruby?
2
+
3
+ require 'concurrent/errors'
4
+ require 'concurrent/utility/engine'
5
+ require 'concurrent/executor/abstract_executor_service'
6
+
7
+ module Concurrent
8
+
9
+ # @!macro abstract_executor_service_public_api
10
+ # @!visibility private
11
+ class JavaExecutorService < AbstractExecutorService
12
+ java_import 'java.lang.Runnable'
13
+
14
+ FALLBACK_POLICY_CLASSES = {
15
+ abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy,
16
+ discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy,
17
+ caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy
18
+ }.freeze
19
+ private_constant :FALLBACK_POLICY_CLASSES
20
+
21
+ def initialize(*args, &block)
22
+ super
23
+ ns_make_executor_runnable
24
+ end
25
+
26
+ def post(*args, &task)
27
+ raise ArgumentError.new('no block given') unless block_given?
28
+ return handle_fallback(*args, &task) unless running?
29
+ @executor.submit_runnable Job.new(args, task)
30
+ true
31
+ rescue Java::JavaUtilConcurrent::RejectedExecutionException
32
+ raise RejectedExecutionError
33
+ end
34
+
35
+ def wait_for_termination(timeout = nil)
36
+ if timeout.nil?
37
+ ok = @executor.awaitTermination(60, java.util.concurrent.TimeUnit::SECONDS) until ok
38
+ true
39
+ else
40
+ @executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
41
+ end
42
+ end
43
+
44
+ def shutdown
45
+ synchronize do
46
+ self.ns_auto_terminate = false
47
+ @executor.shutdown
48
+ nil
49
+ end
50
+ end
51
+
52
+ def kill
53
+ synchronize do
54
+ self.ns_auto_terminate = false
55
+ @executor.shutdownNow
56
+ nil
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def ns_running?
63
+ !(ns_shuttingdown? || ns_shutdown?)
64
+ end
65
+
66
+ def ns_shuttingdown?
67
+ if @executor.respond_to? :isTerminating
68
+ @executor.isTerminating
69
+ else
70
+ false
71
+ end
72
+ end
73
+
74
+ def ns_shutdown?
75
+ @executor.isShutdown || @executor.isTerminated
76
+ end
77
+
78
+ def ns_make_executor_runnable
79
+ if !defined?(@executor.submit_runnable)
80
+ @executor.class.class_eval do
81
+ java_alias :submit_runnable, :submit, [java.lang.Runnable.java_class]
82
+ end
83
+ end
84
+ end
85
+
86
+ class Job
87
+ include Runnable
88
+ def initialize(args, block)
89
+ @args = args
90
+ @block = block
91
+ end
92
+
93
+ def run
94
+ @block.call(*@args)
95
+ end
96
+ end
97
+ private_constant :Job
98
+ end
99
+ end
100
+ end
@@ -1,11 +1,11 @@
1
1
  if Concurrent.on_jruby?
2
2
 
3
- require 'concurrent/executor/executor_service'
3
+ require 'concurrent/executor/java_executor_service'
4
+ require 'concurrent/executor/serial_executor_service'
4
5
 
5
6
  module Concurrent
6
7
 
7
8
  # @!macro single_thread_executor
8
- # @!macro thread_pool_options
9
9
  # @!macro abstract_executor_service_public_api
10
10
  # @!visibility private
11
11
  class JavaSingleThreadExecutor < JavaExecutorService
@@ -16,7 +16,7 @@ if Concurrent.on_jruby?
16
16
  super(opts)
17
17
  end
18
18
 
19
- protected
19
+ private
20
20
 
21
21
  def ns_initialize(opts)
22
22
  @executor = java.util.concurrent.Executors.newSingleThreadExecutor
@@ -1,6 +1,6 @@
1
1
  if Concurrent.on_jruby?
2
2
 
3
- require 'concurrent/executor/executor_service'
3
+ require 'concurrent/executor/java_executor_service'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -87,15 +87,14 @@ if Concurrent.on_jruby?
87
87
  super && !@executor.isTerminating
88
88
  end
89
89
 
90
- protected
90
+ private
91
91
 
92
92
  def ns_initialize(opts)
93
93
  min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
94
94
  max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
95
95
  idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
96
96
  @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
97
- @fallback_policy = opts.fetch(:fallback_policy, opts.fetch(:overflow_policy, :abort))
98
- deprecated ' :overflow_policy is deprecated terminology, please use :fallback_policy instead' if opts.has_key?(:overflow_policy)
97
+ @fallback_policy = opts.fetch(:fallback_policy, :abort)
99
98
 
100
99
  raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE
101
100
  raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE
@@ -0,0 +1,78 @@
1
+ require 'concurrent/executor/abstract_executor_service'
2
+ require 'concurrent/atomic/event'
3
+
4
+ module Concurrent
5
+
6
+ # @!macro abstract_executor_service_public_api
7
+ # @!visibility private
8
+ class RubyExecutorService < AbstractExecutorService
9
+ safe_initialization!
10
+
11
+ def initialize(*args, &block)
12
+ super
13
+ @StopEvent = Event.new
14
+ @StoppedEvent = Event.new
15
+ end
16
+
17
+ def post(*args, &task)
18
+ raise ArgumentError.new('no block given') unless block_given?
19
+ synchronize do
20
+ # If the executor is shut down, reject this task
21
+ return handle_fallback(*args, &task) unless running?
22
+ ns_execute(*args, &task)
23
+ true
24
+ end
25
+ end
26
+
27
+ def shutdown
28
+ synchronize do
29
+ break unless running?
30
+ self.ns_auto_terminate = false
31
+ stop_event.set
32
+ ns_shutdown_execution
33
+ end
34
+ true
35
+ end
36
+
37
+ def kill
38
+ synchronize do
39
+ break if shutdown?
40
+ self.ns_auto_terminate = false
41
+ stop_event.set
42
+ ns_kill_execution
43
+ stopped_event.set
44
+ end
45
+ true
46
+ end
47
+
48
+ def wait_for_termination(timeout = nil)
49
+ stopped_event.wait(timeout)
50
+ end
51
+
52
+ private
53
+
54
+ def stop_event
55
+ @StopEvent
56
+ end
57
+
58
+ def stopped_event
59
+ @StoppedEvent
60
+ end
61
+
62
+ def ns_shutdown_execution
63
+ stopped_event.set
64
+ end
65
+
66
+ def ns_running?
67
+ !stop_event.set?
68
+ end
69
+
70
+ def ns_shuttingdown?
71
+ !(ns_running? || ns_shutdown?)
72
+ end
73
+
74
+ def ns_shutdown?
75
+ stopped_event.set?
76
+ end
77
+ end
78
+ end
@@ -1,78 +1,22 @@
1
- require 'concurrent/executor/executor_service'
1
+ require 'concurrent/executor/ruby_thread_pool_executor'
2
2
 
3
3
  module Concurrent
4
4
 
5
5
  # @!macro single_thread_executor
6
- # @!macro thread_pool_options
7
6
  # @!macro abstract_executor_service_public_api
8
7
  # @!visibility private
9
- class RubySingleThreadExecutor < RubyExecutorService
10
- include SerialExecutorService
8
+ class RubySingleThreadExecutor < RubyThreadPoolExecutor
11
9
 
12
10
  # @!macro single_thread_executor_method_initialize
13
11
  def initialize(opts = {})
14
- super
15
- end
16
-
17
- protected
18
-
19
- def ns_initialize(opts)
20
- @queue = Queue.new
21
- @thread = nil
22
- @fallback_policy = opts.fetch(:fallback_policy, :discard)
23
- raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
24
- self.auto_terminate = opts.fetch(:auto_terminate, true)
25
- end
26
-
27
- # @!visibility private
28
- def execute(*args, &task)
29
- supervise
30
- @queue << [args, task]
31
- end
32
-
33
- # @!visibility private
34
- def shutdown_execution
35
- @queue << :stop
36
- stopped_event.set unless alive?
37
- end
38
-
39
- # @!visibility private
40
- def kill_execution
41
- @queue.clear
42
- @thread.kill if alive?
43
- end
44
-
45
- # @!visibility private
46
- def alive?
47
- @thread && @thread.alive?
48
- end
49
-
50
- # @!visibility private
51
- def supervise
52
- @thread = new_worker_thread unless alive?
53
- end
54
-
55
- # @!visibility private
56
- def new_worker_thread
57
- Thread.new do
58
- Thread.current.abort_on_exception = false
59
- work
60
- end
61
- end
62
-
63
- # @!visibility private
64
- def work
65
- loop do
66
- task = @queue.pop
67
- break if task == :stop
68
- begin
69
- task.last.call(*task.first)
70
- rescue => ex
71
- # let it fail
72
- log DEBUG, ex
73
- end
74
- end
75
- stopped_event.set
12
+ super(
13
+ min_threads: 1,
14
+ max_threads: 1,
15
+ max_queue: 0,
16
+ idletime: DEFAULT_THREAD_IDLETIMEOUT,
17
+ fallback_policy: opts.fetch(:fallback_policy, :discard),
18
+ auto_terminate: opts.fetch(:auto_terminate, true)
19
+ )
76
20
  end
77
21
  end
78
22
  end
@@ -1,8 +1,7 @@
1
1
  require 'thread'
2
-
3
2
  require 'concurrent/atomic/event'
4
3
  require 'concurrent/concern/logging'
5
- require 'concurrent/executor/executor_service'
4
+ require 'concurrent/executor/ruby_executor_service'
6
5
  require 'concurrent/utility/monotonic_time'
7
6
 
8
7
  module Concurrent
@@ -30,15 +29,6 @@ module Concurrent
30
29
  # @!macro thread_pool_executor_attr_reader_min_length
31
30
  attr_reader :min_length
32
31
 
33
- # @!macro thread_pool_executor_attr_reader_largest_length
34
- attr_reader :largest_length
35
-
36
- # @!macro thread_pool_executor_attr_reader_scheduled_task_count
37
- attr_reader :scheduled_task_count
38
-
39
- # @!macro thread_pool_executor_attr_reader_completed_task_count
40
- attr_reader :completed_task_count
41
-
42
32
  # @!macro thread_pool_executor_attr_reader_idletime
43
33
  attr_reader :idletime
44
34
 
@@ -50,6 +40,21 @@ module Concurrent
50
40
  super(opts)
51
41
  end
52
42
 
43
+ # @!macro thread_pool_executor_attr_reader_largest_length
44
+ def largest_length
45
+ synchronize { @largest_length }
46
+ end
47
+
48
+ # @!macro thread_pool_executor_attr_reader_scheduled_task_count
49
+ def scheduled_task_count
50
+ synchronize { @scheduled_task_count }
51
+ end
52
+
53
+ # @!macro thread_pool_executor_attr_reader_completed_task_count
54
+ def completed_task_count
55
+ synchronize { @completed_task_count }
56
+ end
57
+
53
58
  # @!macro executor_service_method_can_overflow_question
54
59
  def can_overflow?
55
60
  synchronize { ns_limited_queue? }
@@ -96,7 +101,12 @@ module Concurrent
96
101
  synchronize { ns_worker_died worker }
97
102
  end
98
103
 
99
- protected
104
+ # @!visibility private
105
+ def worker_task_completed
106
+ synchronize { @completed_task_count += 1 }
107
+ end
108
+
109
+ private
100
110
 
101
111
  # @!visibility private
102
112
  def ns_initialize(opts)
@@ -104,9 +114,8 @@ module Concurrent
104
114
  @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
105
115
  @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
106
116
  @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
107
- @fallback_policy = opts.fetch(:fallback_policy, opts.fetch(:overflow_policy, :abort))
117
+ @fallback_policy = opts.fetch(:fallback_policy, :abort)
108
118
  raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
109
- deprecated ':overflow_policy is deprecated terminology, please use :fallback_policy instead' if opts.has_key?(:overflow_policy)
110
119
 
111
120
  raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE
112
121
  raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE
@@ -144,8 +153,6 @@ module Concurrent
144
153
  # raise unless @ready.empty? || @queue.empty? # assert
145
154
  end
146
155
 
147
- alias_method :execute, :ns_execute
148
-
149
156
  # @!visibility private
150
157
  def ns_shutdown_execution
151
158
  if @pool.empty?
@@ -160,8 +167,6 @@ module Concurrent
160
167
  # raise unless @ready.empty? || @queue.empty? # assert
161
168
  end
162
169
 
163
- alias_method :shutdown_execution, :ns_shutdown_execution
164
-
165
170
  # @!visibility private
166
171
  def ns_kill_execution
167
172
  # TODO log out unprocessed tasks in queue
@@ -171,8 +176,6 @@ module Concurrent
171
176
  @ready.clear
172
177
  end
173
178
 
174
- alias_method :kill_execution, :ns_kill_execution
175
-
176
179
  # tries to assign task to a worker, tries to get one from @ready or to create new one
177
180
  # @return [true, false] if task is assigned to a worker
178
181
  #
@@ -227,8 +230,7 @@ module Concurrent
227
230
  #
228
231
  # @!visibility private
229
232
  def ns_ready_worker(worker, success = true)
230
- @completed_task_count += 1 if success
231
- task_and_args = @queue.shift
233
+ task_and_args = @queue.shift
232
234
  if task_and_args
233
235
  worker << task_and_args
234
236
  else
@@ -329,6 +331,7 @@ module Concurrent
329
331
 
330
332
  def run_task(pool, task, args)
331
333
  task.call(*args)
334
+ pool.worker_task_completed
332
335
  rescue => ex
333
336
  # let it fail
334
337
  log DEBUG, ex