concurrent-ruby 1.1.9 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/Gemfile +2 -8
  4. data/README.md +34 -37
  5. data/Rakefile +48 -69
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  9. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  10. data/lib/concurrent-ruby/concurrent/array.rb +0 -10
  11. data/lib/concurrent-ruby/concurrent/async.rb +1 -0
  12. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
  17. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  18. data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
  19. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +188 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
  27. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
  29. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
  30. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  31. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  33. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  34. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  35. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  36. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  37. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  38. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  39. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  40. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  41. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  42. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
  43. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  44. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
  45. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  46. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  47. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  48. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
  49. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  50. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  51. data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
  52. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  53. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  54. data/lib/concurrent-ruby/concurrent/map.rb +9 -9
  55. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/promise.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
  60. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  61. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
  62. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  63. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  64. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  65. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  66. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  67. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  68. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  69. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
  71. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  72. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  73. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  74. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  75. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  76. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  77. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
  78. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  79. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  80. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  81. data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
  82. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  83. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
  84. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  85. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  86. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
  87. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  88. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  89. metadata +10 -12
  90. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  91. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  92. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  93. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  94. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  95. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  96. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  97. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -1,4 +1,5 @@
1
1
  require 'logger'
2
+ require 'concurrent/atomic/atomic_reference'
2
3
 
3
4
  module Concurrent
4
5
  module Concern
@@ -15,8 +16,6 @@ module Concurrent
15
16
  # @param [String, nil] message when nil block is used to generate the message
16
17
  # @yieldreturn [String] a message
17
18
  def log(level, progname, message = nil, &block)
18
- #NOTE: Cannot require 'concurrent/configuration' above due to circular references.
19
- # Assume that the gem has been initialized if we've gotten this far.
20
19
  logger = if defined?(@logger) && @logger
21
20
  @logger
22
21
  else
@@ -30,3 +29,88 @@ module Concurrent
30
29
  end
31
30
  end
32
31
  end
32
+
33
+ module Concurrent
34
+ extend Concern::Logging
35
+
36
+ # @return [Logger] Logger with provided level and output.
37
+ def self.create_simple_logger(level = Logger::FATAL, output = $stderr)
38
+ # TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
39
+ lambda do |severity, progname, message = nil, &block|
40
+ return false if severity < level
41
+
42
+ message = block ? block.call : message
43
+ formatted_message = case message
44
+ when String
45
+ message
46
+ when Exception
47
+ format "%s (%s)\n%s",
48
+ message.message, message.class, (message.backtrace || []).join("\n")
49
+ else
50
+ message.inspect
51
+ end
52
+
53
+ output.print format "[%s] %5s -- %s: %s\n",
54
+ Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
55
+ Logger::SEV_LABEL[severity],
56
+ progname,
57
+ formatted_message
58
+ true
59
+ end
60
+ end
61
+
62
+ # Use logger created by #create_simple_logger to log concurrent-ruby messages.
63
+ def self.use_simple_logger(level = Logger::FATAL, output = $stderr)
64
+ Concurrent.global_logger = create_simple_logger level, output
65
+ end
66
+
67
+ # @return [Logger] Logger with provided level and output.
68
+ # @deprecated
69
+ def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
70
+ logger = Logger.new(output)
71
+ logger.level = level
72
+ logger.formatter = lambda do |severity, datetime, progname, msg|
73
+ formatted_message = case msg
74
+ when String
75
+ msg
76
+ when Exception
77
+ format "%s (%s)\n%s",
78
+ msg.message, msg.class, (msg.backtrace || []).join("\n")
79
+ else
80
+ msg.inspect
81
+ end
82
+ format "[%s] %5s -- %s: %s\n",
83
+ datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
84
+ severity,
85
+ progname,
86
+ formatted_message
87
+ end
88
+
89
+ lambda do |loglevel, progname, message = nil, &block|
90
+ logger.add loglevel, message, progname, &block
91
+ end
92
+ end
93
+
94
+ # Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
95
+ # @deprecated
96
+ def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
97
+ Concurrent.global_logger = create_stdlib_logger level, output
98
+ end
99
+
100
+ # TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
101
+
102
+ # Suppresses all output when used for logging.
103
+ NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
104
+
105
+ # @!visibility private
106
+ GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN))
107
+ private_constant :GLOBAL_LOGGER
108
+
109
+ def self.global_logger
110
+ GLOBAL_LOGGER.value
111
+ end
112
+
113
+ def self.global_logger=(value)
114
+ GLOBAL_LOGGER.value = value
115
+ end
116
+ end
@@ -1,102 +1,19 @@
1
1
  require 'thread'
2
2
  require 'concurrent/delay'
3
3
  require 'concurrent/errors'
4
- require 'concurrent/atomic/atomic_reference'
5
- require 'concurrent/concern/logging'
6
4
  require 'concurrent/concern/deprecation'
7
5
  require 'concurrent/executor/immediate_executor'
6
+ require 'concurrent/executor/fixed_thread_pool'
8
7
  require 'concurrent/executor/cached_thread_pool'
9
8
  require 'concurrent/utility/processor_counter'
10
9
 
11
10
  module Concurrent
12
- extend Concern::Logging
13
11
  extend Concern::Deprecation
14
12
 
15
13
  autoload :Options, 'concurrent/options'
16
14
  autoload :TimerSet, 'concurrent/executor/timer_set'
17
15
  autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor'
18
16
 
19
- # @return [Logger] Logger with provided level and output.
20
- def self.create_simple_logger(level = Logger::FATAL, output = $stderr)
21
- # TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
22
- lambda do |severity, progname, message = nil, &block|
23
- return false if severity < level
24
-
25
- message = block ? block.call : message
26
- formatted_message = case message
27
- when String
28
- message
29
- when Exception
30
- format "%s (%s)\n%s",
31
- message.message, message.class, (message.backtrace || []).join("\n")
32
- else
33
- message.inspect
34
- end
35
-
36
- output.print format "[%s] %5s -- %s: %s\n",
37
- Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
38
- Logger::SEV_LABEL[severity],
39
- progname,
40
- formatted_message
41
- true
42
- end
43
- end
44
-
45
- # Use logger created by #create_simple_logger to log concurrent-ruby messages.
46
- def self.use_simple_logger(level = Logger::FATAL, output = $stderr)
47
- Concurrent.global_logger = create_simple_logger level, output
48
- end
49
-
50
- # @return [Logger] Logger with provided level and output.
51
- # @deprecated
52
- def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
53
- logger = Logger.new(output)
54
- logger.level = level
55
- logger.formatter = lambda do |severity, datetime, progname, msg|
56
- formatted_message = case msg
57
- when String
58
- msg
59
- when Exception
60
- format "%s (%s)\n%s",
61
- msg.message, msg.class, (msg.backtrace || []).join("\n")
62
- else
63
- msg.inspect
64
- end
65
- format "[%s] %5s -- %s: %s\n",
66
- datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
67
- severity,
68
- progname,
69
- formatted_message
70
- end
71
-
72
- lambda do |loglevel, progname, message = nil, &block|
73
- logger.add loglevel, message, progname, &block
74
- end
75
- end
76
-
77
- # Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
78
- # @deprecated
79
- def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
80
- Concurrent.global_logger = create_stdlib_logger level, output
81
- end
82
-
83
- # TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
84
-
85
- # Suppresses all output when used for logging.
86
- NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
87
-
88
- # @!visibility private
89
- GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN))
90
- private_constant :GLOBAL_LOGGER
91
-
92
- def self.global_logger
93
- GLOBAL_LOGGER.value
94
- end
95
-
96
- def self.global_logger=(value)
97
- GLOBAL_LOGGER.value = value
98
- end
99
-
100
17
  # @!visibility private
101
18
  GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor }
102
19
  private_constant :GLOBAL_FAST_EXECUTOR
@@ -136,14 +53,14 @@ module Concurrent
136
53
  #
137
54
  # @return [ThreadPoolExecutor] the thread pool
138
55
  def self.global_fast_executor
139
- GLOBAL_FAST_EXECUTOR.value
56
+ GLOBAL_FAST_EXECUTOR.value!
140
57
  end
141
58
 
142
59
  # Global thread pool optimized for long, blocking (IO) *tasks*.
143
60
  #
144
61
  # @return [ThreadPoolExecutor] the thread pool
145
62
  def self.global_io_executor
146
- GLOBAL_IO_EXECUTOR.value
63
+ GLOBAL_IO_EXECUTOR.value!
147
64
  end
148
65
 
149
66
  def self.global_immediate_executor
@@ -154,7 +71,7 @@ module Concurrent
154
71
  #
155
72
  # @return [Concurrent::TimerSet] the thread pool
156
73
  def self.global_timer_set
157
- GLOBAL_TIMER_SET.value
74
+ GLOBAL_TIMER_SET.value!
158
75
  end
159
76
 
160
77
  # General access point to global executors.
@@ -1,7 +1,7 @@
1
1
  require 'thread'
2
2
  require 'concurrent/concern/obligation'
3
3
  require 'concurrent/executor/immediate_executor'
4
- require 'concurrent/synchronization'
4
+ require 'concurrent/synchronization/lockable_object'
5
5
 
6
6
  module Concurrent
7
7
 
@@ -67,7 +67,7 @@ module Concurrent
67
67
 
68
68
  # Return the value this object represents after applying the options
69
69
  # specified by the `#set_deref_options` method. If the delayed operation
70
- # raised an exception this method will return nil. The execption object
70
+ # raised an exception this method will return nil. The exception object
71
71
  # can be accessed via the `#reason` method.
72
72
  #
73
73
  # @param [Numeric] timeout the maximum number of seconds to wait
@@ -66,4 +66,9 @@ module Concurrent
66
66
  end
67
67
  end
68
68
 
69
+ # @!macro internal_implementation_note
70
+ class ConcurrentUpdateError < ThreadError
71
+ # frozen pre-allocated backtrace to speed ConcurrentUpdateError
72
+ CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze
73
+ end
69
74
  end
@@ -289,6 +289,7 @@ module Concurrent
289
289
  end
290
290
 
291
291
  if Concurrent.on_jruby?
292
+ require 'concurrent/utility/native_extension_loader'
292
293
 
293
294
  # @!macro internal_implementation_note
294
295
  # @!visibility private
@@ -1,7 +1,7 @@
1
1
  require 'concurrent/errors'
2
2
  require 'concurrent/concern/deprecation'
3
3
  require 'concurrent/executor/executor_service'
4
- require 'concurrent/synchronization'
4
+ require 'concurrent/synchronization/lockable_object'
5
5
 
6
6
  module Concurrent
7
7
 
@@ -75,28 +75,31 @@ module Concurrent
75
75
 
76
76
  private
77
77
 
78
- # Handler which executes the `fallback_policy` once the queue size
79
- # reaches `max_queue`.
78
+ # Returns an action which executes the `fallback_policy` once the queue
79
+ # size reaches `max_queue`. The reason for the indirection of an action
80
+ # is so that the work can be deferred outside of synchronization.
80
81
  #
81
82
  # @param [Array] args the arguments to the task which is being handled.
82
83
  #
83
84
  # @!visibility private
84
- def handle_fallback(*args)
85
+ def fallback_action(*args)
85
86
  case fallback_policy
86
87
  when :abort
87
- raise RejectedExecutionError
88
+ lambda { raise RejectedExecutionError }
88
89
  when :discard
89
- false
90
+ lambda { false }
90
91
  when :caller_runs
91
- begin
92
- yield(*args)
93
- rescue => ex
94
- # let it fail
95
- log DEBUG, ex
96
- end
97
- true
92
+ lambda {
93
+ begin
94
+ yield(*args)
95
+ rescue => ex
96
+ # let it fail
97
+ log DEBUG, ex
98
+ end
99
+ true
100
+ }
98
101
  else
99
- fail "Unknown fallback policy #{fallback_policy}"
102
+ lambda { fail "Unknown fallback policy #{fallback_policy}" }
100
103
  end
101
104
  end
102
105
 
@@ -71,9 +71,16 @@ module Concurrent
71
71
  # @return [Integer] Number of tasks that may be enqueued before reaching `max_queue` and rejecting
72
72
  # new tasks. A value of -1 indicates that the queue may grow without bound.
73
73
 
74
-
75
-
76
-
74
+ # @!macro thread_pool_executor_method_prune_pool
75
+ # Prune the thread pool of unneeded threads
76
+ #
77
+ # What is being pruned is controlled by the min_threads and idletime
78
+ # parameters passed at pool creation time
79
+ #
80
+ # This is a no-op on some pool implementation (e.g. the Java one). The Ruby
81
+ # pool will auto-prune each time a new job is posted. You will need to call
82
+ # this method explicitely in case your application post jobs in bursts (a
83
+ # lot of jobs and then nothing for long periods)
77
84
 
78
85
  # @!macro thread_pool_executor_public_api
79
86
  #
@@ -111,6 +118,9 @@ module Concurrent
111
118
  #
112
119
  # @!method can_overflow?
113
120
  # @!macro executor_service_method_can_overflow_question
121
+ #
122
+ # @!method prune_pool
123
+ # @!macro thread_pool_executor_method_prune_pool
114
124
 
115
125
 
116
126
 
@@ -1,7 +1,7 @@
1
- if Concurrent.on_jruby?
1
+ require 'concurrent/utility/engine'
2
2
 
3
+ if Concurrent.on_jruby?
3
4
  require 'concurrent/errors'
4
- require 'concurrent/utility/engine'
5
5
  require 'concurrent/executor/abstract_executor_service'
6
6
 
7
7
  module Concurrent
@@ -20,7 +20,7 @@ if Concurrent.on_jruby?
20
20
 
21
21
  def post(*args, &task)
22
22
  raise ArgumentError.new('no block given') unless block_given?
23
- return handle_fallback(*args, &task) unless running?
23
+ return fallback_action(*args, &task).call unless running?
24
24
  @executor.submit Job.new(args, task)
25
25
  true
26
26
  rescue Java::JavaUtilConcurrent::RejectedExecutionException
@@ -93,6 +93,10 @@ if Concurrent.on_jruby?
93
93
  super && !@executor.isTerminating
94
94
  end
95
95
 
96
+ # @!macro thread_pool_executor_method_prune_pool
97
+ def prune_pool
98
+ end
99
+
96
100
  private
97
101
 
98
102
  def ns_initialize(opts)
@@ -16,10 +16,16 @@ module Concurrent
16
16
 
17
17
  def post(*args, &task)
18
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)
19
+ deferred_action = synchronize {
20
+ if running?
21
+ ns_execute(*args, &task)
22
+ else
23
+ fallback_action(*args, &task)
24
+ end
25
+ }
26
+ if deferred_action
27
+ deferred_action.call
28
+ else
23
29
  true
24
30
  end
25
31
  end
@@ -93,13 +93,8 @@ module Concurrent
93
93
  end
94
94
 
95
95
  # @!visibility private
96
- def ready_worker(worker)
97
- synchronize { ns_ready_worker worker }
98
- end
99
-
100
- # @!visibility private
101
- def worker_not_old_enough(worker)
102
- synchronize { ns_worker_not_old_enough worker }
96
+ def ready_worker(worker, last_message)
97
+ synchronize { ns_ready_worker worker, last_message }
103
98
  end
104
99
 
105
100
  # @!visibility private
@@ -112,6 +107,11 @@ module Concurrent
112
107
  synchronize { @completed_task_count += 1 }
113
108
  end
114
109
 
110
+ # @!macro thread_pool_executor_method_prune_pool
111
+ def prune_pool
112
+ synchronize { ns_prune_pool }
113
+ end
114
+
115
115
  private
116
116
 
117
117
  # @!visibility private
@@ -156,10 +156,11 @@ module Concurrent
156
156
  if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task)
157
157
  @scheduled_task_count += 1
158
158
  else
159
- handle_fallback(*args, &task)
159
+ return fallback_action(*args, &task)
160
160
  end
161
161
 
162
162
  ns_prune_pool if @next_gc_time < Concurrent.monotonic_time
163
+ nil
163
164
  end
164
165
 
165
166
  # @!visibility private
@@ -192,7 +193,7 @@ module Concurrent
192
193
  # @!visibility private
193
194
  def ns_assign_worker(*args, &task)
194
195
  # keep growing if the pool is not at the minimum yet
195
- worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
196
+ worker, _ = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
196
197
  if worker
197
198
  worker << [task, args]
198
199
  true
@@ -223,7 +224,7 @@ module Concurrent
223
224
  def ns_worker_died(worker)
224
225
  ns_remove_busy_worker worker
225
226
  replacement_worker = ns_add_busy_worker
226
- ns_ready_worker replacement_worker, false if replacement_worker
227
+ ns_ready_worker replacement_worker, Concurrent.monotonic_time, false if replacement_worker
227
228
  end
228
229
 
229
230
  # creates new worker which has to receive work to do after it's added
@@ -242,29 +243,21 @@ module Concurrent
242
243
  # handle ready worker, giving it new job or assigning back to @ready
243
244
  #
244
245
  # @!visibility private
245
- def ns_ready_worker(worker, success = true)
246
+ def ns_ready_worker(worker, last_message, success = true)
246
247
  task_and_args = @queue.shift
247
248
  if task_and_args
248
249
  worker << task_and_args
249
250
  else
250
251
  # stop workers when !running?, do not return them to @ready
251
252
  if running?
252
- @ready.push(worker)
253
+ raise unless last_message
254
+ @ready.push([worker, last_message])
253
255
  else
254
256
  worker.stop
255
257
  end
256
258
  end
257
259
  end
258
260
 
259
- # returns back worker to @ready which was not idle for enough time
260
- #
261
- # @!visibility private
262
- def ns_worker_not_old_enough(worker)
263
- # let's put workers coming from idle_test back to the start (as the oldest worker)
264
- @ready.unshift(worker)
265
- true
266
- end
267
-
268
261
  # removes a worker which is not in not tracked in @ready
269
262
  #
270
263
  # @!visibility private
@@ -278,10 +271,17 @@ module Concurrent
278
271
  #
279
272
  # @!visibility private
280
273
  def ns_prune_pool
281
- return if @pool.size <= @min_length
282
-
283
- last_used = @ready.shift
284
- last_used << :idle_test if last_used
274
+ now = Concurrent.monotonic_time
275
+ stopped_workers = 0
276
+ while !@ready.empty? && (@pool.size - stopped_workers > @min_length)
277
+ worker, last_message = @ready.first
278
+ if now - last_message > self.idletime
279
+ stopped_workers += 1
280
+ @ready.shift
281
+ worker << :stop
282
+ else break
283
+ end
284
+ end
285
285
 
286
286
  @next_gc_time = Concurrent.monotonic_time + @gc_interval
287
287
  end
@@ -330,19 +330,10 @@ module Concurrent
330
330
 
331
331
  def create_worker(queue, pool, idletime)
332
332
  Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime|
333
- last_message = Concurrent.monotonic_time
334
333
  catch(:stop) do
335
334
  loop do
336
335
 
337
336
  case message = my_queue.pop
338
- when :idle_test
339
- if (Concurrent.monotonic_time - last_message) > my_idletime
340
- my_pool.remove_busy_worker(self)
341
- throw :stop
342
- else
343
- my_pool.worker_not_old_enough(self)
344
- end
345
-
346
337
  when :stop
347
338
  my_pool.remove_busy_worker(self)
348
339
  throw :stop
@@ -350,9 +341,7 @@ module Concurrent
350
341
  else
351
342
  task, args = message
352
343
  run_task my_pool, task, args
353
- last_message = Concurrent.monotonic_time
354
-
355
- my_pool.ready_worker(self)
344
+ my_pool.ready_worker(self, Concurrent.monotonic_time)
356
345
  end
357
346
  end
358
347
  end
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -16,10 +16,10 @@ module Concurrent
16
16
 
17
17
  # @return [Array]
18
18
  def execute(*args)
19
- synchronize do
20
- success = false
21
- value = reason = nil
19
+ success = true
20
+ value = reason = nil
22
21
 
22
+ synchronize do
23
23
  begin
24
24
  value = @task.call(*args)
25
25
  success = true
@@ -27,9 +27,9 @@ module Concurrent
27
27
  reason = ex
28
28
  success = false
29
29
  end
30
-
31
- [success, value, reason]
32
30
  end
31
+
32
+ [success, value, reason]
33
33
  end
34
34
  end
35
35
  end
@@ -1,6 +1,6 @@
1
1
  require 'concurrent/errors'
2
2
  require 'concurrent/concern/logging'
3
- require 'concurrent/synchronization'
3
+ require 'concurrent/synchronization/lockable_object'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -1,5 +1,8 @@
1
- require 'concurrent/atomics'
1
+ require 'concurrent/atomic/atomic_boolean'
2
+ require 'concurrent/atomic/atomic_fixnum'
3
+ require 'concurrent/atomic/event'
2
4
  require 'concurrent/executor/executor_service'
5
+ require 'concurrent/executor/ruby_executor_service'
3
6
 
4
7
  module Concurrent
5
8
 
@@ -28,15 +28,6 @@ module Concurrent
28
28
  end
29
29
  JRubyHash
30
30
 
31
- when Concurrent.on_rbx?
32
- require 'monitor'
33
- require 'concurrent/thread_safe/util/data_structures'
34
-
35
- class RbxHash < ::Hash
36
- end
37
- ThreadSafe::Util.make_synchronized_on_rbx RbxHash
38
- RbxHash
39
-
40
31
  when Concurrent.on_truffleruby?
41
32
  require 'concurrent/thread_safe/util/data_structures'
42
33
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/synchronization/abstract_struct'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -3,7 +3,8 @@ require 'concurrent/errors'
3
3
  require 'concurrent/collection/copy_on_write_observer_set'
4
4
  require 'concurrent/concern/obligation'
5
5
  require 'concurrent/concern/observable'
6
- require 'concurrent/synchronization'
6
+ require 'concurrent/executor/safe_task_executor'
7
+ require 'concurrent/synchronization/lockable_object'
7
8
 
8
9
  module Concurrent
9
10