concurrent-ruby 0.8.0 → 0.9.0.pre2

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -2
  3. data/README.md +103 -54
  4. data/lib/concurrent.rb +34 -14
  5. data/lib/concurrent/async.rb +164 -50
  6. data/lib/concurrent/atom.rb +171 -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 +3 -0
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +10 -32
  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 +27 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +25 -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 +226 -112
  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 +10 -0
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +46 -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 +206 -26
  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_cached_thread_pool.rb +18 -16
  46. data/lib/concurrent/executor/java_fixed_thread_pool.rb +11 -18
  47. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  48. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  49. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +9 -18
  50. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +10 -21
  51. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  52. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  53. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  54. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  55. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  56. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  57. data/lib/concurrent/executor/thread_pool_executor.rb +72 -60
  58. data/lib/concurrent/executor/timer_set.rb +96 -84
  59. data/lib/concurrent/executors.rb +1 -1
  60. data/lib/concurrent/future.rb +70 -38
  61. data/lib/concurrent/immutable_struct.rb +89 -0
  62. data/lib/concurrent/ivar.rb +152 -60
  63. data/lib/concurrent/lazy_register.rb +40 -20
  64. data/lib/concurrent/maybe.rb +226 -0
  65. data/lib/concurrent/mutable_struct.rb +227 -0
  66. data/lib/concurrent/mvar.rb +44 -43
  67. data/lib/concurrent/promise.rb +208 -134
  68. data/lib/concurrent/scheduled_task.rb +339 -43
  69. data/lib/concurrent/settable_struct.rb +127 -0
  70. data/lib/concurrent/synchronization.rb +17 -0
  71. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  72. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  73. data/lib/concurrent/synchronization/condition.rb +53 -0
  74. data/lib/concurrent/synchronization/java_object.rb +35 -0
  75. data/lib/concurrent/synchronization/lock.rb +32 -0
  76. data/lib/concurrent/synchronization/monitor_object.rb +24 -0
  77. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  78. data/lib/concurrent/synchronization/object.rb +78 -0
  79. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  80. data/lib/concurrent/timer_task.rb +87 -100
  81. data/lib/concurrent/tvar.rb +42 -38
  82. data/lib/concurrent/utilities.rb +3 -1
  83. data/lib/concurrent/utility/at_exit.rb +97 -0
  84. data/lib/concurrent/utility/engine.rb +40 -0
  85. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  86. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  87. data/lib/concurrent/utility/processor_counter.rb +156 -0
  88. data/lib/concurrent/utility/timeout.rb +18 -14
  89. data/lib/concurrent/utility/timer.rb +11 -6
  90. data/lib/concurrent/version.rb +2 -1
  91. data/lib/concurrent_ruby.rb +1 -0
  92. metadata +47 -83
  93. data/lib/concurrent/actor.rb +0 -103
  94. data/lib/concurrent/actor/behaviour.rb +0 -70
  95. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  96. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  97. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  98. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  99. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  100. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  101. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  102. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  103. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  104. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  105. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  106. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  107. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  108. data/lib/concurrent/actor/context.rb +0 -154
  109. data/lib/concurrent/actor/core.rb +0 -217
  110. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  111. data/lib/concurrent/actor/envelope.rb +0 -41
  112. data/lib/concurrent/actor/errors.rb +0 -27
  113. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  114. data/lib/concurrent/actor/public_delegations.rb +0 -40
  115. data/lib/concurrent/actor/reference.rb +0 -81
  116. data/lib/concurrent/actor/root.rb +0 -37
  117. data/lib/concurrent/actor/type_check.rb +0 -48
  118. data/lib/concurrent/actor/utils.rb +0 -10
  119. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  120. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  121. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  122. data/lib/concurrent/actor/utils/pool.rb +0 -59
  123. data/lib/concurrent/actress.rb +0 -3
  124. data/lib/concurrent/agent.rb +0 -209
  125. data/lib/concurrent/atomic.rb +0 -92
  126. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  127. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  128. data/lib/concurrent/atomic/synchronization.rb +0 -51
  129. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  130. data/lib/concurrent/channel/channel.rb +0 -41
  131. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  132. data/lib/concurrent/channel/waitable_list.rb +0 -40
  133. data/lib/concurrent/channels.rb +0 -5
  134. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  135. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  136. data/lib/concurrent/collections.rb +0 -3
  137. data/lib/concurrent/dereferenceable.rb +0 -108
  138. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  139. data/lib/concurrent/logging.rb +0 -20
  140. data/lib/concurrent/obligation.rb +0 -171
  141. data/lib/concurrent/observable.rb +0 -73
  142. data/lib/concurrent/options_parser.rb +0 -52
  143. data/lib/concurrent/utility/processor_count.rb +0 -152
  144. data/lib/extension_helper.rb +0 -37
@@ -1,128 +1,274 @@
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
+ # Suppresses all output when used for logging.
15
+ NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
15
16
 
16
- # a proc defining how to log messages, its interface has to be:
17
- # lambda { |level, progname, message = nil, &block| _ }
18
- attr_accessor :logger
17
+ # @!visibility private
18
+ GLOBAL_LOGGER = AtomicReference.new(NULL_LOGGER)
19
+ private_constant :GLOBAL_LOGGER
20
+
21
+ # @!visibility private
22
+ GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor(auto_terminate: true) }
23
+ private_constant :GLOBAL_FAST_EXECUTOR
24
+
25
+ # @!visibility private
26
+ GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor(auto_terminate: true) }
27
+ private_constant :GLOBAL_IO_EXECUTOR
19
28
 
20
- # defines if executors should be auto-terminated in at_exit callback
21
- attr_accessor :auto_terminate
29
+ # @!visibility private
30
+ GLOBAL_TIMER_SET = Delay.new { TimerSet.new(auto_terminate: true) }
31
+ private_constant :GLOBAL_TIMER_SET
32
+
33
+ # @!visibility private
34
+ GLOBAL_IMMEDIATE_EXECUTOR = ImmediateExecutor.new
35
+ private_constant :GLOBAL_IMMEDIATE_EXECUTOR
36
+
37
+ def self.global_logger
38
+ GLOBAL_LOGGER.value
39
+ end
40
+
41
+ def self.global_logger=(value)
42
+ GLOBAL_LOGGER.value = value
43
+ end
44
+
45
+ # @return [Logger] Logger with provided level and output.
46
+ def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
47
+ logger = Logger.new(output)
48
+ logger.level = level
49
+ logger.formatter = lambda do |severity, datetime, progname, msg|
50
+ formatted_message = case msg
51
+ when String
52
+ msg
53
+ when Exception
54
+ format "%s (%s)\n%s",
55
+ msg.message, msg.class, (msg.backtrace || []).join("\n")
56
+ else
57
+ msg.inspect
58
+ end
59
+ format "[%s] %5s -- %s: %s\n",
60
+ datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
61
+ severity,
62
+ progname,
63
+ formatted_message
64
+ end
65
+ logger
66
+ end
67
+
68
+ # Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
69
+ def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
70
+ logger = create_stdlib_logger level, output
71
+ Concurrent.global_logger = lambda do |level, progname, message = nil, &block|
72
+ logger.add level, message, progname, &block
73
+ end
74
+ end
75
+
76
+ # Disables AtExit hooks including pool auto-termination hooks.
77
+ # When disabled it will be the application
78
+ # programmer's responsibility to ensure that the hooks
79
+ # are shutdown properly prior to application exit
80
+ # by calling {AtExit.run} method.
81
+ #
82
+ # @note this option should be needed only because of `at_exit` ordering
83
+ # issues which may arise when running some of the testing frameworks.
84
+ # E.g. Minitest's test-suite runs itself in `at_exit` callback which
85
+ # executes after the pools are already terminated. Then auto termination
86
+ # needs to be disabled and called manually after test-suite ends.
87
+ # @note This method should *never* be called
88
+ # from within a gem. It should *only* be used from within the main
89
+ # application and even then it should be used only when necessary.
90
+ # @see AtExit
91
+ def self.disable_at_exit_hooks!
92
+ AtExit.enabled = false
93
+ end
94
+
95
+ def self.disable_executor_auto_termination!
96
+ deprecated_method 'disable_executor_auto_termination!', 'disable_at_exit_hooks!'
97
+ disable_at_exit_hooks!
98
+ end
99
+
100
+ # @return [true,false]
101
+ # @see .disable_executor_auto_termination!
102
+ def self.disable_executor_auto_termination?
103
+ deprecated_method 'disable_executor_auto_termination?', 'Concurrent::AtExit.enabled?'
104
+ AtExit.enabled?
105
+ end
106
+
107
+ # terminates all pools and blocks until they are terminated
108
+ # @see .disable_executor_auto_termination!
109
+ def self.terminate_pools!
110
+ deprecated_method 'terminate_pools!', 'Concurrent::AtExit.run'
111
+ AtExit.run
112
+ end
113
+
114
+ # Global thread pool optimized for short, fast *operations*.
115
+ #
116
+ # @return [ThreadPoolExecutor] the thread pool
117
+ def self.global_fast_executor
118
+ GLOBAL_FAST_EXECUTOR.value
119
+ end
120
+
121
+ # Global thread pool optimized for long, blocking (IO) *tasks*.
122
+ #
123
+ # @return [ThreadPoolExecutor] the thread pool
124
+ def self.global_io_executor
125
+ GLOBAL_IO_EXECUTOR.value
126
+ end
127
+
128
+ def self.global_immediate_executor
129
+ GLOBAL_IMMEDIATE_EXECUTOR
130
+ end
131
+
132
+ # Global thread pool user for global *timers*.
133
+ #
134
+ # @return [Concurrent::TimerSet] the thread pool
135
+ #
136
+ # @see Concurrent::timer
137
+ def self.global_timer_set
138
+ GLOBAL_TIMER_SET.value
139
+ end
140
+
141
+ # General access point to global executors.
142
+ # @param [Symbol, Executor] executor_identifier symbols:
143
+ # - :fast - {Concurrent.global_fast_executor}
144
+ # - :io - {Concurrent.global_io_executor}
145
+ # - :immediate - {Concurrent.global_immediate_executor}
146
+ # @return [Executor]
147
+ def self.executor(executor_identifier)
148
+ Executor.executor(executor_identifier)
149
+ end
150
+
151
+ def self.new_fast_executor(opts = {})
152
+ FixedThreadPool.new(
153
+ [2, Concurrent.processor_count].max,
154
+ auto_terminate: opts.fetch(:auto_terminate, true),
155
+ idletime: 60, # 1 minute
156
+ max_queue: 0, # unlimited
157
+ fallback_policy: :abort # shouldn't matter -- 0 max queue
158
+ )
159
+ end
160
+
161
+ def self.new_io_executor(opts = {})
162
+ ThreadPoolExecutor.new(
163
+ min_threads: [2, Concurrent.processor_count].max,
164
+ max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
165
+ # max_threads: 1000,
166
+ auto_terminate: opts.fetch(:auto_terminate, true),
167
+ idletime: 60, # 1 minute
168
+ max_queue: 0, # unlimited
169
+ fallback_policy: :abort # shouldn't matter -- 0 max queue
170
+ )
171
+ end
172
+
173
+ # A gem-level configuration object.
174
+ class Configuration
175
+ include Concern::Deprecation
22
176
 
23
177
  # Create a new configuration object.
24
178
  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
179
  end
32
180
 
33
181
  # if assigned to {#logger}, it will log nothing.
182
+ # @deprecated Use Concurrent::NULL_LOGGER instead
34
183
  def no_logger
35
- lambda { |level, progname, message = nil, &block| }
184
+ deprecated_method 'Concurrent.configuration.no_logger', 'Concurrent::NULL_LOGGER'
185
+ NULL_LOGGER
36
186
  end
37
187
 
38
- # Global thread pool optimized for short *tasks*.
188
+ # a proc defining how to log messages, its interface has to be:
189
+ # lambda { |level, progname, message = nil, &block| _ }
39
190
  #
40
- # @return [ThreadPoolExecutor] the thread pool
41
- def global_task_pool
42
- @global_task_pool.value
191
+ # @deprecated Use Concurrent.global_logger instead
192
+ def logger
193
+ deprecated_method 'Concurrent.configuration.logger', 'Concurrent.global_logger'
194
+ Concurrent.global_logger.value
43
195
  end
44
196
 
45
- # Global thread pool optimized for long *operations*.
197
+ # a proc defining how to log messages, its interface has to be:
198
+ # lambda { |level, progname, message = nil, &block| _ }
46
199
  #
47
- # @return [ThreadPoolExecutor] the thread pool
200
+ # @deprecated Use Concurrent.global_logger instead
201
+ def logger=(value)
202
+ deprecated_method 'Concurrent.configuration.logger=', 'Concurrent.global_logger='
203
+ Concurrent.global_logger = value
204
+ end
205
+
206
+ # @deprecated Use Concurrent.global_io_executor instead
207
+ def global_task_pool
208
+ deprecated_method 'Concurrent.configuration.global_task_pool', 'Concurrent.global_io_executor'
209
+ Concurrent.global_io_executor
210
+ end
211
+
212
+ # @deprecated Use Concurrent.global_fast_executor instead
48
213
  def global_operation_pool
49
- @global_operation_pool.value
214
+ deprecated_method 'Concurrent.configuration.global_operation_pool', 'Concurrent.global_fast_executor'
215
+ Concurrent.global_fast_executor
50
216
  end
51
217
 
52
- # Global thread pool optimized for *timers*
53
- #
54
- # @return [ThreadPoolExecutor] the thread pool
55
- #
56
- # @see Concurrent::timer
218
+ # @deprecated Use Concurrent.global_timer_set instead
57
219
  def global_timer_set
58
- @global_timer_set.value
220
+ deprecated_method 'Concurrent.configuration.global_timer_set', 'Concurrent.global_timer_set'
221
+ Concurrent.global_timer_set
59
222
  end
60
223
 
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
224
+ # @deprecated Replacing global thread pools is deprecated.
225
+ # Use the :executor constructor option instead.
75
226
  def global_task_pool=(executor)
76
- @global_task_pool.reconfigure { executor } or
227
+ deprecated 'Replacing global thread pools is deprecated. Use the :executor constructor option instead.'
228
+ GLOBAL_IO_EXECUTOR.reconfigure { executor } or
77
229
  raise ConfigurationError.new('global task pool was already set')
78
230
  end
79
231
 
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
232
+ # @deprecated Replacing global thread pools is deprecated.
233
+ # Use the :executor constructor option instead.
94
234
  def global_operation_pool=(executor)
95
- @global_operation_pool.reconfigure { executor } or
235
+ deprecated 'Replacing global thread pools is deprecated. Use the :executor constructor option instead.'
236
+ GLOBAL_FAST_EXECUTOR.reconfigure { executor } or
96
237
  raise ConfigurationError.new('global operation pool was already set')
97
238
  end
98
239
 
240
+ # @deprecated Use Concurrent.new_io_executor instead
99
241
  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
- )
242
+ deprecated_method 'Concurrent.configuration.new_task_pool', 'Concurrent.new_io_executor'
243
+ Concurrent.new_io_executor
107
244
  end
108
245
 
246
+ # @deprecated Use Concurrent.new_fast_executor instead
109
247
  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
- )
248
+ deprecated_method 'Concurrent.configuration.new_operation_pool', 'Concurrent.new_fast_executor'
249
+ Concurrent.new_fast_executor
250
+ end
251
+
252
+ # @deprecated Use Concurrent.disable_auto_termination_of_global_executors! instead
253
+ def auto_terminate=(value)
254
+ deprecated_method 'Concurrent.configuration.auto_terminate=', 'Concurrent.disable_auto_termination_of_global_executors!'
255
+ Concurrent.disable_auto_termination_of_global_executors! if !value
256
+ end
257
+
258
+ # @deprecated Use Concurrent.auto_terminate_global_executors? instead
259
+ def auto_terminate
260
+ deprecated_method 'Concurrent.configuration.auto_terminate', 'Concurrent.auto_terminate_global_executors?'
261
+ Concurrent.auto_terminate_global_executors?
117
262
  end
118
263
  end
119
264
 
120
265
  # create the default configuration on load
121
- @configuration = Atomic.new Configuration.new
266
+ CONFIGURATION = AtomicReference.new(Configuration.new)
267
+ private_constant :CONFIGURATION
122
268
 
123
269
  # @return [Configuration]
124
270
  def self.configuration
125
- @configuration.value
271
+ CONFIGURATION.value
126
272
  end
127
273
 
128
274
  # Perform gem-level configuration.
@@ -132,36 +278,4 @@ module Concurrent
132
278
  def self.configure
133
279
  yield(configuration)
134
280
  end
135
-
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
141
-
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
166
- end
167
281
  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