concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +234 -109
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -1,128 +1,271 @@
1
1
  require 'thread'
2
- require 'concurrent/delay'
2
+ require 'concurrent/atomics'
3
3
  require 'concurrent/errors'
4
- require 'concurrent/atomic'
5
- require 'concurrent/executor/immediate_executor'
6
- require 'concurrent/executor/thread_pool_executor'
7
- require 'concurrent/executor/timer_set'
8
- require 'concurrent/utility/processor_count'
4
+ require 'concurrent/executors'
5
+ require 'concurrent/concern/deprecation'
6
+ require 'concurrent/concern/logging'
7
+ require 'concurrent/utility/at_exit'
8
+ require 'concurrent/utility/processor_counter'
9
9
 
10
10
  module Concurrent
11
- extend Logging
11
+ extend Concern::Logging
12
+ extend Concern::Deprecation
12
13
 
13
- # A gem-level configuration object.
14
- class Configuration
14
+ # @return [Logger] Logger with provided level and output.
15
+ def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
16
+ logger = Logger.new(output)
17
+ logger.level = level
18
+ logger.formatter = lambda do |severity, datetime, progname, msg|
19
+ formatted_message = case msg
20
+ when String
21
+ msg
22
+ when Exception
23
+ format "%s (%s)\n%s",
24
+ msg.message, msg.class, (msg.backtrace || []).join("\n")
25
+ else
26
+ msg.inspect
27
+ end
28
+ format "[%s] %5s -- %s: %s\n",
29
+ datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
30
+ severity,
31
+ progname,
32
+ formatted_message
33
+ end
15
34
 
16
- # a proc defining how to log messages, its interface has to be:
17
- # lambda { |level, progname, message = nil, &block| _ }
18
- attr_accessor :logger
35
+ lambda do |level, progname, message = nil, &block|
36
+ logger.add level, message, progname, &block
37
+ end
38
+ end
39
+
40
+ # Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
41
+ def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
42
+ Concurrent.global_logger = create_stdlib_logger level, output
43
+ end
44
+
45
+ # Suppresses all output when used for logging.
46
+ NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
47
+
48
+ # @!visibility private
49
+ GLOBAL_LOGGER = AtomicReference.new(create_stdlib_logger(Logger::WARN))
50
+ private_constant :GLOBAL_LOGGER
51
+
52
+ def self.global_logger
53
+ GLOBAL_LOGGER.value
54
+ end
55
+
56
+ def self.global_logger=(value)
57
+ GLOBAL_LOGGER.value = value
58
+ end
59
+
60
+ # @!visibility private
61
+ GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor(auto_terminate: true) }
62
+ private_constant :GLOBAL_FAST_EXECUTOR
63
+
64
+ # @!visibility private
65
+ GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor(auto_terminate: true) }
66
+ private_constant :GLOBAL_IO_EXECUTOR
67
+
68
+ # @!visibility private
69
+ GLOBAL_TIMER_SET = Delay.new { TimerSet.new(auto_terminate: true) }
70
+ private_constant :GLOBAL_TIMER_SET
71
+
72
+ # @!visibility private
73
+ GLOBAL_IMMEDIATE_EXECUTOR = ImmediateExecutor.new
74
+ private_constant :GLOBAL_IMMEDIATE_EXECUTOR
75
+
76
+ # Disables AtExit handlers including pool auto-termination handlers.
77
+ # When disabled it will be the application programmer's responsibility
78
+ # to ensure that the handlers are shutdown properly prior to application
79
+ # exit by calling {AtExit.run} method.
80
+ #
81
+ # @note this option should be needed only because of `at_exit` ordering
82
+ # issues which may arise when running some of the testing frameworks.
83
+ # E.g. Minitest's test-suite runs itself in `at_exit` callback which
84
+ # executes after the pools are already terminated. Then auto termination
85
+ # needs to be disabled and called manually after test-suite ends.
86
+ # @note This method should *never* be called
87
+ # from within a gem. It should *only* be used from within the main
88
+ # application and even then it should be used only when necessary.
89
+ # @see AtExit
90
+ def self.disable_at_exit_handlers!
91
+ AtExit.enabled = false
92
+ end
93
+
94
+ def self.disable_executor_auto_termination!
95
+ deprecated_method 'disable_executor_auto_termination!', 'disable_at_exit_handlers!'
96
+ disable_at_exit_handlers!
97
+ end
98
+
99
+ # @return [true,false]
100
+ # @see .disable_executor_auto_termination!
101
+ def self.disable_executor_auto_termination?
102
+ deprecated_method 'disable_executor_auto_termination?', 'Concurrent::AtExit.enabled?'
103
+ AtExit.enabled?
104
+ end
105
+
106
+ # terminates all pools and blocks until they are terminated
107
+ # @see .disable_executor_auto_termination!
108
+ def self.terminate_pools!
109
+ deprecated_method 'terminate_pools!', 'Concurrent::AtExit.run'
110
+ AtExit.run
111
+ end
112
+
113
+ # Global thread pool optimized for short, fast *operations*.
114
+ #
115
+ # @return [ThreadPoolExecutor] the thread pool
116
+ def self.global_fast_executor
117
+ GLOBAL_FAST_EXECUTOR.value
118
+ end
119
+
120
+ # Global thread pool optimized for long, blocking (IO) *tasks*.
121
+ #
122
+ # @return [ThreadPoolExecutor] the thread pool
123
+ def self.global_io_executor
124
+ GLOBAL_IO_EXECUTOR.value
125
+ end
126
+
127
+ def self.global_immediate_executor
128
+ GLOBAL_IMMEDIATE_EXECUTOR
129
+ end
130
+
131
+ # Global thread pool user for global *timers*.
132
+ #
133
+ # @return [Concurrent::TimerSet] the thread pool
134
+ def self.global_timer_set
135
+ GLOBAL_TIMER_SET.value
136
+ end
137
+
138
+ # General access point to global executors.
139
+ # @param [Symbol, Executor] executor_identifier symbols:
140
+ # - :fast - {Concurrent.global_fast_executor}
141
+ # - :io - {Concurrent.global_io_executor}
142
+ # - :immediate - {Concurrent.global_immediate_executor}
143
+ # @return [Executor]
144
+ def self.executor(executor_identifier)
145
+ Executor.executor(executor_identifier)
146
+ end
147
+
148
+ def self.new_fast_executor(opts = {})
149
+ FixedThreadPool.new(
150
+ [2, Concurrent.processor_count].max,
151
+ auto_terminate: opts.fetch(:auto_terminate, true),
152
+ idletime: 60, # 1 minute
153
+ max_queue: 0, # unlimited
154
+ fallback_policy: :abort # shouldn't matter -- 0 max queue
155
+ )
156
+ end
19
157
 
20
- # defines if executors should be auto-terminated in at_exit callback
21
- attr_accessor :auto_terminate
158
+ def self.new_io_executor(opts = {})
159
+ ThreadPoolExecutor.new(
160
+ min_threads: [2, Concurrent.processor_count].max,
161
+ max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
162
+ # max_threads: 1000,
163
+ auto_terminate: opts.fetch(:auto_terminate, true),
164
+ idletime: 60, # 1 minute
165
+ max_queue: 0, # unlimited
166
+ fallback_policy: :abort # shouldn't matter -- 0 max queue
167
+ )
168
+ end
169
+
170
+ # A gem-level configuration object.
171
+ class Configuration
172
+ include Concern::Deprecation
22
173
 
23
174
  # Create a new configuration object.
24
175
  def initialize
25
- immediate_executor = ImmediateExecutor.new
26
- @global_task_pool = Delay.new(executor: immediate_executor) { new_task_pool }
27
- @global_operation_pool = Delay.new(executor: immediate_executor) { new_operation_pool }
28
- @global_timer_set = Delay.new(executor: immediate_executor) { Concurrent::TimerSet.new }
29
- @logger = no_logger
30
- @auto_terminate = true
31
176
  end
32
177
 
33
178
  # if assigned to {#logger}, it will log nothing.
179
+ # @deprecated Use Concurrent::NULL_LOGGER instead
34
180
  def no_logger
35
- lambda { |level, progname, message = nil, &block| }
181
+ deprecated_method 'Concurrent.configuration.no_logger', 'Concurrent::NULL_LOGGER'
182
+ NULL_LOGGER
36
183
  end
37
184
 
38
- # Global thread pool optimized for short *tasks*.
185
+ # a proc defining how to log messages, its interface has to be:
186
+ # lambda { |level, progname, message = nil, &block| _ }
39
187
  #
40
- # @return [ThreadPoolExecutor] the thread pool
41
- def global_task_pool
42
- @global_task_pool.value
188
+ # @deprecated Use Concurrent.global_logger instead
189
+ def logger
190
+ deprecated_method 'Concurrent.configuration.logger', 'Concurrent.global_logger'
191
+ Concurrent.global_logger.value
43
192
  end
44
193
 
45
- # Global thread pool optimized for long *operations*.
194
+ # a proc defining how to log messages, its interface has to be:
195
+ # lambda { |level, progname, message = nil, &block| _ }
46
196
  #
47
- # @return [ThreadPoolExecutor] the thread pool
197
+ # @deprecated Use Concurrent.global_logger instead
198
+ def logger=(value)
199
+ deprecated_method 'Concurrent.configuration.logger=', 'Concurrent.global_logger='
200
+ Concurrent.global_logger = value
201
+ end
202
+
203
+ # @deprecated Use Concurrent.global_io_executor instead
204
+ def global_task_pool
205
+ deprecated_method 'Concurrent.configuration.global_task_pool', 'Concurrent.global_io_executor'
206
+ Concurrent.global_io_executor
207
+ end
208
+
209
+ # @deprecated Use Concurrent.global_fast_executor instead
48
210
  def global_operation_pool
49
- @global_operation_pool.value
211
+ deprecated_method 'Concurrent.configuration.global_operation_pool', 'Concurrent.global_fast_executor'
212
+ Concurrent.global_fast_executor
50
213
  end
51
214
 
52
- # Global thread pool optimized for *timers*
53
- #
54
- # @return [ThreadPoolExecutor] the thread pool
55
- #
56
- # @see Concurrent::timer
215
+ # @deprecated Use Concurrent.global_timer_set instead
57
216
  def global_timer_set
58
- @global_timer_set.value
217
+ deprecated_method 'Concurrent.configuration.global_timer_set', 'Concurrent.global_timer_set'
218
+ Concurrent.global_timer_set
59
219
  end
60
220
 
61
- # Global thread pool optimized for short *tasks*.
62
- #
63
- # A global thread pool must be set as soon as the gem is loaded. Setting a new
64
- # thread pool once tasks and operations have been post can lead to unpredictable
65
- # results. The first time a task/operation is post a new thread pool will be
66
- # created using the default configuration. Once set the thread pool cannot be
67
- # changed. Thus, explicitly setting the thread pool must occur *before* any
68
- # tasks/operations are post else an exception will be raised.
69
- #
70
- # @param [Executor] executor the executor to be used for this thread pool
71
- #
72
- # @return [ThreadPoolExecutor] the new thread pool
73
- #
74
- # @raise [Concurrent::ConfigurationError] if this thread pool has already been set
221
+ # @deprecated Replacing global thread pools is deprecated.
222
+ # Use the :executor constructor option instead.
75
223
  def global_task_pool=(executor)
76
- @global_task_pool.reconfigure { executor } or
224
+ deprecated 'Replacing global thread pools is deprecated. Use the :executor constructor option instead.'
225
+ GLOBAL_IO_EXECUTOR.reconfigure { executor } or
77
226
  raise ConfigurationError.new('global task pool was already set')
78
227
  end
79
228
 
80
- # Global thread pool optimized for long *operations*.
81
- #
82
- # A global thread pool must be set as soon as the gem is loaded. Setting a new
83
- # thread pool once tasks and operations have been post can lead to unpredictable
84
- # results. The first time a task/operation is post a new thread pool will be
85
- # created using the default configuration. Once set the thread pool cannot be
86
- # changed. Thus, explicitly setting the thread pool must occur *before* any
87
- # tasks/operations are post else an exception will be raised.
88
- #
89
- # @param [Executor] executor the executor to be used for this thread pool
90
- #
91
- # @return [ThreadPoolExecutor] the new thread pool
92
- #
93
- # @raise [Concurrent::ConfigurationError] if this thread pool has already been set
229
+ # @deprecated Replacing global thread pools is deprecated.
230
+ # Use the :executor constructor option instead.
94
231
  def global_operation_pool=(executor)
95
- @global_operation_pool.reconfigure { executor } or
232
+ deprecated 'Replacing global thread pools is deprecated. Use the :executor constructor option instead.'
233
+ GLOBAL_FAST_EXECUTOR.reconfigure { executor } or
96
234
  raise ConfigurationError.new('global operation pool was already set')
97
235
  end
98
236
 
237
+ # @deprecated Use Concurrent.new_io_executor instead
99
238
  def new_task_pool
100
- Concurrent::ThreadPoolExecutor.new(
101
- min_threads: [2, Concurrent.processor_count].max,
102
- max_threads: [20, Concurrent.processor_count * 15].max,
103
- idletime: 2 * 60, # 2 minutes
104
- max_queue: 0, # unlimited
105
- fallback_policy: :abort # raise an exception
106
- )
239
+ deprecated_method 'Concurrent.configuration.new_task_pool', 'Concurrent.new_io_executor'
240
+ Concurrent.new_io_executor
107
241
  end
108
242
 
243
+ # @deprecated Use Concurrent.new_fast_executor instead
109
244
  def new_operation_pool
110
- Concurrent::ThreadPoolExecutor.new(
111
- min_threads: [2, Concurrent.processor_count].max,
112
- max_threads: [2, Concurrent.processor_count].max,
113
- idletime: 10 * 60, # 10 minutes
114
- max_queue: [20, Concurrent.processor_count * 15].max,
115
- fallback_policy: :abort # raise an exception
116
- )
245
+ deprecated_method 'Concurrent.configuration.new_operation_pool', 'Concurrent.new_fast_executor'
246
+ Concurrent.new_fast_executor
247
+ end
248
+
249
+ # @deprecated Use Concurrent.disable_executor_auto_termination! instead
250
+ def auto_terminate=(value)
251
+ deprecated_method 'Concurrent.configuration.auto_terminate=', 'Concurrent.disable_executor_auto_termination!'
252
+ Concurrent.disable_executor_auto_termination! if !value
253
+ end
254
+
255
+ # @deprecated Use Concurrent.auto_terminate_global_executors? instead
256
+ def auto_terminate
257
+ deprecated_method 'Concurrent.configuration.auto_terminate', 'Concurrent.auto_terminate_global_executors?'
258
+ Concurrent.auto_terminate_global_executors?
117
259
  end
118
260
  end
119
261
 
120
262
  # create the default configuration on load
121
- @configuration = Atomic.new Configuration.new
263
+ CONFIGURATION = AtomicReference.new(Configuration.new)
264
+ private_constant :CONFIGURATION
122
265
 
123
266
  # @return [Configuration]
124
267
  def self.configuration
125
- @configuration.value
268
+ CONFIGURATION.value
126
269
  end
127
270
 
128
271
  # Perform gem-level configuration.
@@ -133,35 +276,17 @@ module Concurrent
133
276
  yield(configuration)
134
277
  end
135
278
 
136
- def self.finalize_global_executors
137
- self.finalize_executor(self.configuration.global_timer_set)
138
- self.finalize_executor(self.configuration.global_task_pool)
139
- self.finalize_executor(self.configuration.global_operation_pool)
140
- end
279
+ # for dependency reasons this check cannot be in concurrent/synchronization
280
+ if Concurrent.on_jruby?
281
+ require 'java'
141
282
 
142
- private
143
-
144
- # Attempt to properly shutdown the given executor using the `shutdown` or
145
- # `kill` method when available.
146
- #
147
- # @param [Executor] executor the executor to shutdown
148
- #
149
- # @return [Boolean] `true` if the executor is successfully shut down or `nil`, else `false`
150
- def self.finalize_executor(executor)
151
- return true if executor.nil?
152
- if executor.respond_to?(:shutdown)
153
- executor.shutdown
154
- elsif executor.respond_to?(:kill)
155
- executor.kill
156
- end
157
- true
158
- rescue => ex
159
- log DEBUG, ex
160
- false
161
- end
162
-
163
- # set exit hook to shutdown global thread pools
164
- at_exit do
165
- finalize_global_executors if configuration.auto_terminate
283
+ version_string = java.lang.System.getProperties['java.runtime.version']
284
+ version = version_string.split('.', 3)[0..1].map(&:to_i)
285
+ if (version <=> [1, 8]) < 0
286
+ deprecated <<-TXT.gsub(/^\s*\|/, '').chop, 0
287
+ |Java 7 is deprecated, please use Java 8.
288
+ |Java 7 support is only best effort, it may not work. It will be removed in next release (1.0).
289
+ TXT
290
+ end
166
291
  end
167
292
  end
@@ -1,6 +1,5 @@
1
1
  require 'concurrent/future'
2
2
  require 'concurrent/atomic/atomic_fixnum'
3
- require 'concurrent/executor/per_thread_executor'
4
3
 
5
4
  module Concurrent
6
5
 
@@ -32,7 +31,7 @@ module Concurrent
32
31
  # @raise [ArgumentError] if no block is given
33
32
  # @raise [ArgumentError] if any of the inputs are not `IVar`s
34
33
  def dataflow(*inputs, &block)
35
- dataflow_with(Concurrent.configuration.global_operation_pool, *inputs, &block)
34
+ dataflow_with(Concurrent.global_io_executor, *inputs, &block)
36
35
  end
37
36
  module_function :dataflow
38
37
 
@@ -42,7 +41,7 @@ module Concurrent
42
41
  module_function :dataflow_with
43
42
 
44
43
  def dataflow!(*inputs, &block)
45
- dataflow_with!(Concurrent.configuration.global_task_pool, *inputs, &block)
44
+ dataflow_with!(Concurrent.global_io_executor, *inputs, &block)
46
45
  end
47
46
  module_function :dataflow!
48
47
 
@@ -1,17 +1,21 @@
1
1
  require 'thread'
2
- require 'concurrent/obligation'
3
- require 'concurrent/options_parser'
2
+ require 'concurrent/configuration'
3
+ require 'concurrent/concern/obligation'
4
+ require 'concurrent/executor/executor'
5
+ require 'concurrent/executor/immediate_executor'
6
+ require 'concurrent/synchronization'
4
7
 
5
8
  module Concurrent
6
9
 
7
- # Lazy evaluation of a block yielding an immutable result. Useful for expensive
8
- # operations that may never be needed.
10
+ # Lazy evaluation of a block yielding an immutable result. Useful for
11
+ # expensive operations that may never be needed. It may be non-blocking,
12
+ # supports the `Concern::Obligation` interface, and accepts the injection of
13
+ # custom executor upon which to execute the block. Processing of
14
+ # block will be deferred until the first time `#value` is called.
15
+ # At that time the caller can choose to return immediately and let
16
+ # the block execute asynchronously, block indefinitely, or block
17
+ # with a timeout.
9
18
  #
10
- # A `Delay` is similar to `Future` but solves a different problem.
11
- # Where a `Future` schedules an operation for immediate execution and
12
- # performs the operation asynchronously, a `Delay` (as the name implies)
13
- # delays execution of the operation until the result is actually needed.
14
- #
15
19
  # When a `Delay` is created its state is set to `pending`. The value and
16
20
  # reason are both `nil`. The first time the `#value` method is called the
17
21
  # enclosed opration will be run and the calling thread will block. Other
@@ -22,68 +26,155 @@ module Concurrent
22
26
  # return the cached value. The operation will only be run once. This means that
23
27
  # any side effects created by the operation will only happen once as well.
24
28
  #
25
- # `Delay` includes the `Concurrent::Dereferenceable` mixin to support thread
29
+ # `Delay` includes the `Concurrent::Concern::Dereferenceable` mixin to support thread
26
30
  # safety of the reference returned by `#value`.
27
31
  #
28
- # @since 0.6.0
32
+ # @!macro copy_options
29
33
  #
30
- # @see Concurrent::Dereferenceable
34
+ # @!macro [attach] delay_note_regarding_blocking
35
+ # @note The default behavior of `Delay` is to block indefinitely when
36
+ # calling either `value` or `wait`, executing the delayed operation on
37
+ # the current thread. This makes the `timeout` value completely
38
+ # irrelevant. To enable non-blocking behavior, use the `executor`
39
+ # constructor option. This will cause the delayed operation to be
40
+ # execute on the given executor, allowing the call to timeout.
31
41
  #
32
- # @see http://clojuredocs.org/clojure_core/clojure.core/delay
33
- # @see http://aphyr.com/posts/306-clojure-from-the-ground-up-state
34
- class Delay
35
- include Obligation
42
+ # @see Concurrent::Concern::Dereferenceable
43
+ class Delay < Synchronization::Object
44
+ include Concern::Obligation
45
+
46
+ # NOTE: Because the global thread pools are lazy-loaded with these objects
47
+ # there is a performance hit every time we post a new task to one of these
48
+ # thread pools. Subsequently it is critical that `Delay` perform as fast
49
+ # as possible post-completion. This class has been highly optimized using
50
+ # the benchmark script `examples/lazy_and_delay.rb`. Do NOT attempt to
51
+ # DRY-up this class or perform other refactoring with running the
52
+ # benchmarks and ensuring that performance is not negatively impacted.
36
53
 
37
54
  # Create a new `Delay` in the `:pending` state.
38
55
  #
39
- # @yield the delayed operation to perform
56
+ # @!macro executor_and_deref_options
40
57
  #
41
- # @param [Hash] opts the options to create a message with
42
- # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
43
- # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
44
- # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
45
- # returning the value returned from the proc
58
+ # @yield the delayed operation to perform
46
59
  #
47
60
  # @raise [ArgumentError] if no block is given
48
61
  def initialize(opts = {}, &block)
49
62
  raise ArgumentError.new('no block given') unless block_given?
63
+ super(&nil)
64
+ synchronize { ns_initialize(opts, &block) }
65
+ end
50
66
 
51
- init_obligation
52
- @state = :pending
53
- @task = block
54
- set_deref_options(opts)
55
- @task_executor = OptionsParser.get_task_executor_from(opts)
56
- @computing = false
67
+ # Return the value this object represents after applying the options
68
+ # specified by the `#set_deref_options` method. If the delayed operation
69
+ # raised an exception this method will return nil. The execption object
70
+ # can be accessed via the `#reason` method.
71
+ #
72
+ # @param [Numeric] timeout the maximum number of seconds to wait
73
+ # @return [Object] the current value of the object
74
+ #
75
+ # @!macro delay_note_regarding_blocking
76
+ def value(timeout = nil)
77
+ if @task_executor
78
+ super
79
+ else
80
+ # this function has been optimized for performance and
81
+ # should not be modified without running new benchmarks
82
+ mutex.synchronize do
83
+ execute = @computing = true unless @computing
84
+ if execute
85
+ begin
86
+ set_state(true, @task.call, nil)
87
+ rescue => ex
88
+ set_state(false, nil, ex)
89
+ end
90
+ end
91
+ end
92
+ if @do_nothing_on_deref
93
+ @value
94
+ else
95
+ apply_deref_options(@value)
96
+ end
97
+ end
98
+ end
99
+
100
+ # Return the value this object represents after applying the options
101
+ # specified by the `#set_deref_options` method. If the delayed operation
102
+ # raised an exception, this method will raise that exception (even when)
103
+ # the operation has already been executed).
104
+ #
105
+ # @param [Numeric] timeout the maximum number of seconds to wait
106
+ # @return [Object] the current value of the object
107
+ # @raise [Exception] when `#rejected?` raises `#reason`
108
+ #
109
+ # @!macro delay_note_regarding_blocking
110
+ def value!(timeout = nil)
111
+ if @task_executor
112
+ super
113
+ else
114
+ result = value
115
+ raise @reason if @reason
116
+ result
117
+ end
57
118
  end
58
119
 
59
- def wait(timeout)
60
- execute_task_once
61
- super timeout
120
+ # Return the value this object represents after applying the options
121
+ # specified by the `#set_deref_options` method.
122
+ #
123
+ # @param [Integer] timeout (nil) the maximum number of seconds to wait for
124
+ # the value to be computed. When `nil` the caller will block indefinitely.
125
+ #
126
+ # @return [Object] self
127
+ #
128
+ # @!macro delay_note_regarding_blocking
129
+ def wait(timeout = nil)
130
+ if @task_executor
131
+ execute_task_once
132
+ super(timeout)
133
+ else
134
+ value
135
+ end
136
+ self
62
137
  end
63
138
 
64
- # reconfigures the block returning the value if still #incomplete?
139
+ # Reconfigures the block returning the value if still `#incomplete?`
140
+ #
65
141
  # @yield the delayed operation to perform
66
142
  # @return [true, false] if success
67
143
  def reconfigure(&block)
68
- mutex.lock
69
- raise ArgumentError.new('no block given') unless block_given?
70
- unless @computing
71
- @task = block
72
- true
73
- else
74
- false
144
+ mutex.synchronize do
145
+ raise ArgumentError.new('no block given') unless block_given?
146
+ unless @computing
147
+ @task = block
148
+ true
149
+ else
150
+ false
151
+ end
75
152
  end
76
- ensure
77
- mutex.unlock
153
+ end
154
+
155
+ protected
156
+
157
+ def ns_initialize(opts, &block)
158
+ init_obligation(self)
159
+ set_deref_options(opts)
160
+ @task_executor = Executor.executor_from_options(opts)
161
+
162
+ @task = block
163
+ @state = :pending
164
+ @computing = false
78
165
  end
79
166
 
80
167
  private
81
168
 
82
- def execute_task_once
83
- mutex.lock
84
- execute = @computing = true unless @computing
85
- task = @task
86
- mutex.unlock
169
+ # @!visibility private
170
+ def execute_task_once # :nodoc:
171
+ # this function has been optimized for performance and
172
+ # should not be modified without running new benchmarks
173
+ execute = task = nil
174
+ mutex.synchronize do
175
+ execute = @computing = true unless @computing
176
+ task = @task
177
+ end
87
178
 
88
179
  if execute
89
180
  @task_executor.post do
@@ -93,10 +184,10 @@ module Concurrent
93
184
  rescue => ex
94
185
  reason = ex
95
186
  end
96
- mutex.lock
97
- set_state success, result, reason
98
- event.set
99
- mutex.unlock
187
+ mutex.synchronize do
188
+ set_state(success, result, reason)
189
+ event.set
190
+ end
100
191
  end
101
192
  end
102
193
  end