concurrent-ruby 1.1.8 → 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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/Gemfile +2 -8
  4. data/README.md +49 -28
  5. data/Rakefile +59 -75
  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/collection/map/truffleruby_map_backend.rb +14 -0
  37. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  38. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  39. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  40. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  41. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  42. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  43. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  44. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
  45. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  46. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
  47. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  48. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  49. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  50. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
  51. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  52. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  53. data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
  54. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  55. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  56. data/lib/concurrent-ruby/concurrent/map.rb +18 -8
  57. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  60. data/lib/concurrent-ruby/concurrent/promise.rb +2 -1
  61. data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
  62. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  63. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
  64. data/lib/concurrent-ruby/concurrent/set.rb +12 -14
  65. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  66. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  67. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  68. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  69. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  71. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  72. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
  73. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +18 -5
  74. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  75. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  76. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  77. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  78. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  79. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
  80. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +16 -27
  81. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  82. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  83. data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
  84. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  85. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
  86. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  87. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  88. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
  89. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  90. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  91. metadata +11 -12
  92. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  93. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  94. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  95. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  96. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  97. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -65
  98. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  99. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -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
 
@@ -1,6 +1,5 @@
1
1
  require 'thread'
2
2
  require 'concurrent/constants'
3
- require 'concurrent/synchronization'
4
3
  require 'concurrent/utility/engine'
5
4
 
6
5
  module Concurrent
@@ -10,14 +9,20 @@ module Concurrent
10
9
  # @!visibility private
11
10
  MapImplementation = case
12
11
  when Concurrent.on_jruby?
12
+ require 'concurrent/utility/native_extension_loader'
13
13
  # noinspection RubyResolve
14
14
  JRubyMapBackend
15
15
  when Concurrent.on_cruby?
16
16
  require 'concurrent/collection/map/mri_map_backend'
17
17
  MriMapBackend
18
- when Concurrent.on_rbx? || Concurrent.on_truffleruby?
19
- require 'concurrent/collection/map/atomic_reference_map_backend'
20
- AtomicReferenceMapBackend
18
+ when Concurrent.on_truffleruby?
19
+ if defined?(::TruffleRuby::ConcurrentMap)
20
+ require 'concurrent/collection/map/truffleruby_map_backend'
21
+ TruffleRubyMapBackend
22
+ else
23
+ require 'concurrent/collection/map/atomic_reference_map_backend'
24
+ AtomicReferenceMapBackend
25
+ end
21
26
  else
22
27
  warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
23
28
  require 'concurrent/collection/map/synchronized_map_backend'
@@ -114,7 +119,7 @@ module Concurrent
114
119
  # @return [true, false] true if deleted
115
120
  # @!macro map.atomic_method
116
121
 
117
-
122
+ #
118
123
  def initialize(options = nil, &block)
119
124
  if options.kind_of?(::Hash)
120
125
  validate_options_hash!(options)
@@ -143,8 +148,15 @@ module Concurrent
143
148
  end
144
149
  end
145
150
 
151
+ # Set a value with key
152
+ # @param [Object] key
153
+ # @param [Object] value
154
+ # @return [Object] the new value
155
+ def []=(key, value)
156
+ super
157
+ end
158
+
146
159
  alias_method :get, :[]
147
- # TODO (pitr-ch 30-Oct-2018): doc
148
160
  alias_method :put, :[]=
149
161
 
150
162
  # Get a value with key, or default_value when key is absent,
@@ -187,7 +199,6 @@ module Concurrent
187
199
  # @yieldparam key [Object]
188
200
  # @yieldreturn [Object] default value
189
201
  # @return [Object] the value or default value
190
- # @!macro map.atomic_method_with_block
191
202
  def fetch_or_store(key, default_value = NULL)
192
203
  fetch(key) do
193
204
  put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
@@ -271,7 +282,6 @@ module Concurrent
271
282
  each_pair { |k, v| return k if v == value }
272
283
  nil
273
284
  end unless method_defined?(:key)
274
- alias_method :index, :key if RUBY_VERSION < '1.9'
275
285
 
276
286
  # Is map empty?
277
287
  # @return [true, false]
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/concern/dereferenceable'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -66,7 +66,7 @@ module Concurrent
66
66
  # Start by requiring promises
67
67
  #
68
68
  # ```ruby
69
- # require 'concurrent'
69
+ # require 'concurrent/promise'
70
70
  # ```
71
71
  #
72
72
  # Then create one
@@ -250,6 +250,7 @@ module Concurrent
250
250
  realize(@promise_body)
251
251
  end
252
252
  else
253
+ compare_and_set_state(:pending, :unscheduled)
253
254
  @parent.execute
254
255
  end
255
256
  self
@@ -1,7 +1,8 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
  require 'concurrent/atomic/atomic_boolean'
3
3
  require 'concurrent/atomic/atomic_fixnum'
4
4
  require 'concurrent/collection/lock_free_stack'
5
+ require 'concurrent/configuration'
5
6
  require 'concurrent/errors'
6
7
  require 'concurrent/re_include'
7
8
 
@@ -313,7 +314,7 @@ module Concurrent
313
314
  end
314
315
 
315
316
  # @!macro promises.shortcut.on
316
- # @return [Future]
317
+ # @return [Event]
317
318
  def any_event(*futures_and_or_events)
318
319
  any_event_on default_executor, *futures_and_or_events
319
320
  end
@@ -892,7 +893,7 @@ module Concurrent
892
893
  private
893
894
 
894
895
  def rejected_resolution(raise_on_reassign, state)
895
- Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
+ raise Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
897
  return false
897
898
  end
898
899
 
@@ -1298,7 +1299,7 @@ module Concurrent
1298
1299
 
1299
1300
  # @!macro promise.param.raise_on_reassign
1300
1301
  # @param [Boolean] raise_on_reassign should method raise exception if already resolved
1301
- # @return [self, false] false is returner when raise_on_reassign is false and the receiver
1302
+ # @return [self, false] false is returned when raise_on_reassign is false and the receiver
1302
1303
  # is already resolved.
1303
1304
  #
1304
1305
 
@@ -2074,8 +2075,8 @@ module Concurrent
2074
2075
 
2075
2076
  private
2076
2077
 
2077
- def resolvable?(countdown, future, index)
2078
- future.fulfilled? ||
2078
+ def resolvable?(countdown, event_or_future, index)
2079
+ (event_or_future.is_a?(Event) ? event_or_future.resolved? : event_or_future.fulfilled?) ||
2079
2080
  # inlined super from BlockedPromise
2080
2081
  countdown.zero?
2081
2082
  end
@@ -31,6 +31,8 @@ module Concurrent
31
31
  #
32
32
  # C1.new.respond_to? :a # => false
33
33
  # C2.new.respond_to? :a # => true
34
+ #
35
+ # @!visibility private
34
36
  module ReInclude
35
37
  # @!visibility private
36
38
  def included(base)
@@ -57,30 +57,43 @@ module Concurrent
57
57
  #
58
58
  # @example Basic usage
59
59
  #
60
- # require 'concurrent'
61
- # require 'thread' # for Queue
62
- # require 'open-uri' # for open(uri)
60
+ # require 'concurrent/scheduled_task'
61
+ # require 'csv'
62
+ # require 'open-uri'
63
63
  #
64
64
  # class Ticker
65
- # def get_year_end_closing(symbol, year)
66
- # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
67
- # data = open(uri) {|f| f.collect{|line| line.strip } }
68
- # data[1].split(',')[4].to_f
69
- # end
65
+ # def get_year_end_closing(symbol, year, api_key)
66
+ # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv"
67
+ # data = []
68
+ # csv = URI.parse(uri).read
69
+ # if csv.include?('call frequency')
70
+ # return :rate_limit_exceeded
71
+ # end
72
+ # CSV.parse(csv, headers: true) do |row|
73
+ # data << row['close'].to_f if row['timestamp'].include?(year.to_s)
74
+ # end
75
+ # year_end = data.first
76
+ # year_end
77
+ # rescue => e
78
+ # p e
79
+ # end
70
80
  # end
71
81
  #
82
+ # api_key = ENV['ALPHAVANTAGE_KEY']
83
+ # abort(error_message) unless api_key
84
+ #
72
85
  # # Future
73
- # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
86
+ # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) }
74
87
  # price.state #=> :pending
75
- # sleep(1) # do other stuff
76
- # price.value #=> 63.65
77
- # price.state #=> :fulfilled
88
+ # price.pending? #=> true
89
+ # price.value(0) #=> nil (does not block)
78
90
  #
79
- # # ScheduledTask
80
- # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
81
- # task.state #=> :pending
82
- # sleep(3) # do other stuff
83
- # task.value #=> 25.96
91
+ # sleep(1) # do other stuff
92
+ #
93
+ # price.value #=> 63.65 (after blocking if necessary)
94
+ # price.state #=> :fulfilled
95
+ # price.fulfilled? #=> true
96
+ # price.value #=> 63.65
84
97
  #
85
98
  # @example Successful task execution
86
99
  #
@@ -19,13 +19,19 @@ module Concurrent
19
19
  #
20
20
  # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
21
21
 
22
-
23
22
  # @!macro internal_implementation_note
24
23
  SetImplementation = case
25
24
  when Concurrent.on_cruby?
26
- # Because MRI never runs code in parallel, the existing
27
- # non-thread-safe structures should usually work fine.
28
- ::Set
25
+ # The CRuby implementation of Set is written in Ruby itself and is
26
+ # not thread safe for certain methods.
27
+ require 'monitor'
28
+ require 'concurrent/thread_safe/util/data_structures'
29
+
30
+ class CRubySet < ::Set
31
+ end
32
+
33
+ ThreadSafe::Util.make_synchronized_on_cruby CRubySet
34
+ CRubySet
29
35
 
30
36
  when Concurrent.on_jruby?
31
37
  require 'jruby/synchronized'
@@ -33,16 +39,8 @@ module Concurrent
33
39
  class JRubySet < ::Set
34
40
  include JRuby::Synchronized
35
41
  end
36
- JRubySet
37
-
38
- when Concurrent.on_rbx?
39
- require 'monitor'
40
- require 'concurrent/thread_safe/util/data_structures'
41
42
 
42
- class RbxSet < ::Set
43
- end
44
- ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
45
- RbxSet
43
+ JRubySet
46
44
 
47
45
  when Concurrent.on_truffleruby?
48
46
  require 'concurrent/thread_safe/util/data_structures'
@@ -50,7 +48,7 @@ module Concurrent
50
48
  class TruffleRubySet < ::Set
51
49
  end
52
50
 
53
- ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet
51
+ ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet
54
52
  TruffleRubySet
55
53
 
56
54
  else
@@ -1,6 +1,6 @@
1
- require 'concurrent/synchronization/abstract_struct'
2
1
  require 'concurrent/errors'
3
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/abstract_struct'
3
+ require 'concurrent/synchronization/lockable_object'
4
4
 
5
5
  module Concurrent
6
6